Merge branch 'dev' of lab.si.usi.ch:sa4-2020/the-sanmarinoes/frontend into frontend-userstory1-fil

This commit is contained in:
Claudio Maggioni 2020-05-22 00:03:40 +02:00
commit b179115c15
81 changed files with 4508 additions and 3445 deletions

View file

@ -31,7 +31,8 @@ sonar-scanner:
- dev - dev
script: script:
- cd smart-hut - cd smart-hut
- yarn sonar-scanner -Dsonar.host.url=$SONAR_URL -Dsonar.login=$SONAR_LOGIN -Dsonar.projectName=$CI_PROJECT_PATH_SLUG -Dsonar.projectKey=$CI_PROJECT_PATH_SLUG -Dsonar.exclusion=cypress -Dsonar.sources=src -Dsonar.javascript.file.suffixes=.js,.jsx -Dsonar.sourceEncoding=UTF-8 -Dsonar.scm.disabled=True - yarn eslint:report || true
- yarn sonar-scanner -Dsonar.host.url=$SONAR_URL -Dsonar.login=$SONAR_LOGIN -Dsonar.projectName=$CI_PROJECT_PATH_SLUG -Dsonar.projectKey=$CI_PROJECT_PATH_SLUG -Dsonar.exclusion=cypress -Dsonar.sources=src -Dsonar.javascript.file.suffixes=.js,.jsx -Dsonar.sourceEncoding=UTF-8 -Dsonar.scm.disabled=True -Dsonar.eslint.reportPaths=eslint-report.json
smartHut_deploy: smartHut_deploy:
stage: deploy stage: deploy

View file

@ -1,10 +1,11 @@
#!/bin/sh #!/bin/sh
FILES=$(git diff --cached --name-only --diff-filter=ACMR "*.js" "*.jsx" | sed 's| |\\ |g') FILES=$(git diff --cached --name-only --diff-filter=ACMR | sed 's| |\\ |g')
[ -z "$FILES" ] && exit 0
# Prettify all selected files # Prettify all selected files
echo "$FILES" | xargs ./smart-hut/node_modules/.bin/prettier --write cd $(git rev-parse --show-toplevel)/smart-hut
npm run eslint-fix
cd ..
# Add back the modified/prettified files to staging # Add back the modified/prettified files to staging
echo "$FILES" | xargs git add echo "$FILES" | xargs git add

54
smart-hut/.eslintrc Normal file
View file

@ -0,0 +1,54 @@
{
"parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"rules": {
"semi": "error",
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
"react/jsx-no-bind": [
"error",
{
"allowArrowFunctions": true,
"allowBind": false,
"ignoreRefs": true
}
],
"react/no-did-update-set-state": "error",
"react/no-unknown-property": "error",
"react/no-unused-prop-types": "error",
"react/prop-types": "error",
"react/react-in-jsx-scope": "error",
"no-unused-expressions": 0,
"chai-friendly/no-unused-expressions": 2,
"indent": [0, 4]
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"airbnb"
],
"env": {
"node": true,
"browser": true,
"jest": true
},
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"settings": {
"react": {
"version": "detect"
}
},
"plugins": ["react", "chai-friendly"]
}

File diff suppressed because it is too large Load diff

View file

@ -37,6 +37,9 @@
"start": "react-scripts start", "start": "react-scripts start",
"build": "react-scripts build", "build": "react-scripts build",
"test": "react-scripts test", "test": "react-scripts test",
"eslint": "eslint src",
"eslint-fix": "eslint --fix src",
"eslint:report": "eslint src -f json -o eslint-report.json",
"eject": "react-scripts eject" "eject": "react-scripts eject"
}, },
"eslintConfig": { "eslintConfig": {
@ -56,6 +59,8 @@
}, },
"devDependencies": { "devDependencies": {
"prettier": "2.0.1", "prettier": "2.0.1",
"sonarqube-scanner": "^2.6.0" "sonarqube-scanner": "^2.6.0",
"eslint-config-airbnb": "^18.1.0",
"eslint-plugin-chai-friendly": "^0.6.0"
} }
} }

View file

@ -1,28 +1,29 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import { BrowserRouter, Switch, Route, Redirect } from "react-router-dom"; import {
import Home from "./views/Home"; BrowserRouter, Switch, Route, Redirect,
import Dashboard from "./views/Dashboard"; } from 'react-router-dom';
import Signup from "./views/Signup"; import queryString from 'query-string';
import Login from "./views/Login"; import { connect } from 'react-redux';
import FourOhFour from "./views/FourOhFour"; import Home from './views/Home';
import ForgotPass from "./views/Forgot-password"; import Dashboard from './views/Dashboard';
import ChangePass from "./views/Forgot-pass-reset"; import Signup from './views/Signup';
import ConfirmForgotPasswrod from "./views/ConfirmForgotPassword"; import Login from './views/Login';
import ConfirmRegistration from "./views/ConfirmRegistration"; import FourOhFour from './views/FourOhFour';
import ConfirmResetPassword from "./views/ConfirmResetPassword"; import ForgotPass from './views/Forgot-password';
import Instruction from "./views/Instruction"; import ConfirmForgotPasswrod from './views/ConfirmForgotPassword';
import Videocam from "./views/Videocam"; import ConfirmRegistration from './views/ConfirmRegistration';
import queryString from "query-string"; import ConfirmResetPassword from './views/ConfirmResetPassword';
import { RemoteService } from "./remote"; import Instruction from './views/Instruction';
import { connect } from "react-redux"; import Videocam from './views/Videocam';
import { RemoteService } from './remote';
class App extends Component { class App extends Component {
constructor(props, context) { constructor(props, context) {
super(props, context); super(props, context);
this.state = { this.state = {
query: "", query: '',
info: "", info: '',
}; };
} }
@ -36,7 +37,7 @@ class App extends Component {
} }
render() { render() {
console.log("rendering root", this.props.loggedIn, this.state.query); console.log('rendering root', this.props.loggedIn, this.state.query);
return ( return (
<BrowserRouter> <BrowserRouter>
<Switch> <Switch>
@ -49,7 +50,7 @@ class App extends Component {
{this.props.loggedIn ? <Dashboard /> : <Redirect to="/login" />} {this.props.loggedIn ? <Dashboard /> : <Redirect to="/login" />}
</Route> </Route>
<Route path="/forgot-password"> <Route path="/forgot-password">
<ForgotPass /> <ForgotPass type="FPassword1" />
</Route> </Route>
<Route path="/sent-email"> <Route path="/sent-email">
<ConfirmForgotPasswrod /> <ConfirmForgotPasswrod />
@ -60,9 +61,11 @@ class App extends Component {
<Route path="/instruction"> <Route path="/instruction">
<Instruction /> <Instruction />
</Route> </Route>
<Route path="/forgot-pass-reset"> </Route> <Route path="/forgot-pass-reset">
<ForgotPass type="FPassword1" />
</Route>
<Route path="/password-reset"> <Route path="/password-reset">
<ChangePass query={this.state.query} /> <ForgotPass type="FPassword2" query={this.state.query} />
</Route> </Route>
<Route path="/conf-reset-pass"> <Route path="/conf-reset-pass">
<ConfirmResetPassword /> <ConfirmResetPassword />

View file

@ -1,19 +1,19 @@
import React from "react"; import React from 'react';
import { render } from "@testing-library/react"; import { render } from '@testing-library/react';
import { Router } from "react-router"; import { Router } from 'react-router';
import { createMemoryHistory } from "history"; import { createMemoryHistory } from 'history';
import App from "./App"; import { Provider } from 'react-redux';
import { Provider } from "react-redux"; import App from './App';
import smartHutStore from "./store"; import smartHutStore from './store';
test("redirects to homepage", () => { test('redirects to homepage', () => {
const history = createMemoryHistory(); const history = createMemoryHistory();
render( render(
<Router history={history}> <Router history={history}>
<Provider store={smartHutStore}> <Provider store={smartHutStore}>
<App /> <App />
</Provider> </Provider>
</Router> </Router>,
); );
expect(history.location.pathname).toBe("/"); expect(history.location.pathname).toBe('/');
}); });

View file

@ -1,34 +1,34 @@
import React from "react"; import React from 'react';
import PropTypes from "prop-types"; import PropTypes from 'prop-types';
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from '@material-ui/core/styles';
import Paper from "@material-ui/core/Paper"; import Paper from '@material-ui/core/Paper';
import Typography from "@material-ui/core/Typography"; import Typography from '@material-ui/core/Typography';
import Grid from "@material-ui/core/Grid"; import Grid from '@material-ui/core/Grid';
import Link from "@material-ui/core/Link"; import Link from '@material-ui/core/Link';
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
mainFeaturedPost: { mainFeaturedPost: {
position: "relative", position: 'relative',
backgroundColor: theme.palette.grey[800], backgroundColor: theme.palette.grey[800],
color: theme.palette.common.white, color: theme.palette.common.white,
marginBottom: theme.spacing(4), marginBottom: theme.spacing(4),
backgroundImage: "img/banner.jpg", backgroundImage: 'img/banner.jpg',
backgroundSize: "cover", backgroundSize: 'cover',
backgroundRepeat: "no-repeat", backgroundRepeat: 'no-repeat',
backgroundPosition: "center", backgroundPosition: 'center',
}, },
overlay: { overlay: {
position: "absolute", position: 'absolute',
top: 0, top: 0,
bottom: 0, bottom: 0,
right: 0, right: 0,
left: 0, left: 0,
backgroundColor: "rgba(0,0,0,.3)", backgroundColor: 'rgba(0,0,0,.3)',
}, },
mainFeaturedPostContent: { mainFeaturedPostContent: {
position: "relative", position: 'relative',
padding: theme.spacing(3), padding: theme.spacing(3),
[theme.breakpoints.up("md")]: { [theme.breakpoints.up('md')]: {
padding: theme.spacing(6), padding: theme.spacing(6),
paddingRight: 0, paddingRight: 0,
}, },
@ -47,7 +47,7 @@ export default function Banner(props) {
{/* Increase the priority of the hero background image */} {/* Increase the priority of the hero background image */}
{ {
<img <img
style={{ display: "none" }} style={{ display: 'none' }}
src={post.image} src={post.image}
alt={post.imageText} alt={post.imageText}
/> />

View file

@ -1,15 +1,15 @@
import React from 'react' import React from 'react';
import { Dropdown } from 'semantic-ui-react' import { Dropdown } from 'semantic-ui-react';
const options = [ const options = [
{ key: 'Living Room', text: 'Living Room', value: 'Living Room' }, { key: 'Living Room', text: 'Living Room', value: 'Living Room' },
{ key: 'Kitchen', text: 'Kitchen', value: 'Kitchen' }, { key: 'Kitchen', text: 'Kitchen', value: 'Kitchen' },
{ key: 'Garden', text: 'Garden', value: 'Garden' }, { key: 'Garden', text: 'Garden', value: 'Garden' },
{ key: 'Bedroom 1', text: 'Bedroom 1', value: 'Bedroom 1' }, { key: 'Bedroom 1', text: 'Bedroom 1', value: 'Bedroom 1' },
] ];
const DropdownSimulation = () => ( const DropdownSimulation = () => (
<Dropdown placeholder='Skills' fluid multiple selection options={options} /> <Dropdown placeholder="Skills" fluid multiple selection options={options} />
) );
export default DropdownSimulation export default DropdownSimulation;

View file

@ -1,82 +0,0 @@
import { Dropdown } from "semantic-ui-react";
import React, { Component } from "react";
export default class FilterDevices extends Component {
render() {
const tagOptions = [
{
key: "regularLight",
text: "regularLight",
value: "regularLight",
label: { color: "red", empty: true, circular: true },
},
{
key: "dimmableLight",
text: "dimmableLight",
value: "dimmableLight",
label: { color: "blue", empty: true, circular: true },
},
{
key: "buttonDimmer",
text: "buttonDimmer",
value: "buttonDimmer",
label: { color: "black", empty: true, circular: true },
},
{
key: "knobDimmer",
text: "knobDimmer",
value: "knobDimmer",
label: { color: "purple", empty: true, circular: true },
},
{
key: "motionSensor",
text: "motionSensor",
value: "motionSensor",
label: { color: "orange", empty: true, circular: true },
},
{
key: "sensor",
text: "sensor",
value: "sensor",
label: { empty: true, circular: true },
},
{
key: "smartPlug",
text: "smartPlug",
value: "smartPlug",
label: { color: "pink", empty: true, circular: true },
},
{
key: "switch",
text: "switch",
value: "switch",
label: { color: "green", empty: true, circular: true },
},
];
return (
<Dropdown
text="Filter Devices"
icon="filter"
floating
labeled
button
className="icon"
>
<Dropdown.Menu>
<Dropdown.Divider />
<Dropdown.Header icon="tags" content="Tag Device" />
<Dropdown.Menu scrolling>
{tagOptions.map((option) => (
<Dropdown.Item
key={option.value}
{...option}
onClick={this.props.filterDevices}
/>
))}
</Dropdown.Menu>
</Dropdown.Menu>
</Dropdown>
);
}
}

View file

@ -1,4 +1,4 @@
import React from "react"; import React from 'react';
export default function Footer() { export default function Footer() {
return ( return (

View file

@ -1,12 +1,12 @@
import React from "react"; import React from 'react';
import PropTypes from "prop-types"; import PropTypes from 'prop-types';
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from '@material-ui/core/styles';
import Toolbar from "@material-ui/core/Toolbar"; import Toolbar from '@material-ui/core/Toolbar';
import Button from "@material-ui/core/Button"; import Button from '@material-ui/core/Button';
import IconButton from "@material-ui/core/IconButton"; import IconButton from '@material-ui/core/IconButton';
import SearchIcon from "@material-ui/icons/Search"; import SearchIcon from '@material-ui/icons/Search';
import Typography from "@material-ui/core/Typography"; import Typography from '@material-ui/core/Typography';
import Link from "@material-ui/core/Link"; import Link from '@material-ui/core/Link';
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
toolbar: { toolbar: {
@ -16,8 +16,8 @@ const useStyles = makeStyles((theme) => ({
flex: 1, flex: 1,
}, },
toolbarSecondary: { toolbarSecondary: {
justifyContent: "space-between", justifyContent: 'space-between',
overflowX: "auto", overflowX: 'auto',
}, },
toolbarLink: { toolbarLink: {
padding: theme.spacing(1), padding: theme.spacing(1),
@ -30,7 +30,7 @@ export default function Header(props) {
const { sections, title } = props; const { sections, title } = props;
return ( return (
<React.Fragment> <>
<Toolbar className={classes.toolbar}> <Toolbar className={classes.toolbar}>
<Typography <Typography
component="h2" component="h2"
@ -60,7 +60,7 @@ export default function Header(props) {
</Link> </Link>
))} ))}
</Toolbar> </Toolbar>
<Button size="small" variant="outlined" style={{ margin: "0 1rem" }}> <Button size="small" variant="outlined" style={{ margin: '0 1rem' }}>
Login Login
</Button> </Button>
@ -68,7 +68,7 @@ export default function Header(props) {
Sign up Sign up
</Button> </Button>
</Toolbar> </Toolbar>
</React.Fragment> </>
); );
} }

View file

@ -1,4 +1,4 @@
import React from "react"; import React from 'react';
import { import {
Grid, Grid,
Divider, Divider,
@ -6,17 +6,17 @@ import {
Label, Label,
Responsive, Responsive,
Checkbox, Checkbox,
} from "semantic-ui-react"; Segment, Image,
import { Segment, Image } from "semantic-ui-react"; } from 'semantic-ui-react';
import { RemoteService } from "../remote"; import { withRouter } from 'react-router-dom';
import { withRouter } from "react-router-dom"; import { connect } from 'react-redux';
import { connect } from "react-redux"; import { RemoteService } from '../remote';
import SimulationPanel from "./SimulationPanel"; import SimulationPanel from './SimulationPanel';
const IconHomeImage = () => ( const IconHomeImage = () => (
<Image <Image
src="smart-home.png" src="smart-home.png"
style={{ width: "50px", height: "auto" }} style={{ width: '50px', height: 'auto' }}
centered centered
as="a" as="a"
href="/" href="/"
@ -34,35 +34,31 @@ export class MyHeader extends React.Component {
} }
logout() { logout() {
this.props.logout().then(() => this.props.history.push("/")); this.props.logout().then(() => this.props.history.push('/'));
} }
getInfo() { getInfo() {
this.props this.props
.fetchUserInfo() .fetchUserInfo()
.catch((err) => console.error("MyHeader fetch user info error", err)); .catch((err) => console.error('MyHeader fetch user info error', err));
} }
setCameraEnabled(val) { setCameraEnabled(val) {
let enabled = { const enabled = {
cameraEnabled: val, cameraEnabled: val,
}; };
this.props this.props
.userPermissions(enabled) .userPermissions(enabled)
.then(() => this.getInfo()) .then(() => this.getInfo())
.catch((err) => console.error("Camera enabled", err)); .catch((err) => console.error('Camera enabled', err));
} }
openSimulationPanel = () => { openSimulationPanel = () => {
this.setState((state) => { this.setState((state) => ({ simulationPanel: true }));
return { simulationPanel: true };
});
}; };
closeSimulationPanel = () => { closeSimulationPanel = () => {
this.setState((state) => { this.setState((state) => ({ simulationPanel: undefined }));
return { simulationPanel: undefined };
});
}; };
@ -85,7 +81,7 @@ export class MyHeader extends React.Component {
<Divider /> <Divider />
<Grid.Row> <Grid.Row>
<div onClick={this.openSimulationPanel}> <div onClick={this.openSimulationPanel}>
<Button > Similuation Panel </Button> <Button> Similuation Panel </Button>
</div> </div>
</Grid.Row> </Grid.Row>
</Grid.Column> </Grid.Column>
@ -107,9 +103,9 @@ export class MyHeader extends React.Component {
<Segment <Segment
compact compact
style={{ style={{
margin: "auto", margin: 'auto',
marginTop: "1em", marginTop: '1em',
textAlign: "center", textAlign: 'center',
}} }}
> >
<Checkbox <Checkbox
@ -137,7 +133,7 @@ export class MyHeader extends React.Component {
<Divider /> <Divider />
<Grid.Row> <Grid.Row>
<div onClick={this.openSimulationPanel}> <div onClick={this.openSimulationPanel}>
<Button > Similuation Panel </Button> <Button> Similuation Panel </Button>
</div> </div>
</Grid.Row> </Grid.Row>
</Grid.Column> </Grid.Column>
@ -156,9 +152,9 @@ export class MyHeader extends React.Component {
<Segment <Segment
compact compact
style={{ style={{
margin: "auto", margin: 'auto',
marginTop: "1em", marginTop: '1em',
textAlign: "center", textAlign: 'center',
}} }}
> >
<Checkbox <Checkbox
@ -179,11 +175,11 @@ export class MyHeader extends React.Component {
const mapStateToProps = (state, _) => ({ const mapStateToProps = (state, _) => ({
username: username:
state.userInfo && state.userInfo.username ? state.userInfo.username : "", state.userInfo && state.userInfo.username ? state.userInfo.username : '',
cameraEnabled: state.userInfo ? state.userInfo.cameraEnabled : false, cameraEnabled: state.userInfo ? state.userInfo.cameraEnabled : false,
}); });
const LoginContainer = connect( const LoginContainer = connect(
mapStateToProps, mapStateToProps,
RemoteService RemoteService,
)(withRouter(MyHeader)); )(withRouter(MyHeader));
export default LoginContainer; export default LoginContainer;

View file

@ -1,5 +1,5 @@
import _ from "lodash"; import _ from 'lodash';
import React, { Component } from "react"; import React, { Component } from 'react';
import { import {
Container, Container,
Icon, Icon,
@ -7,7 +7,7 @@ import {
Menu, Menu,
Sidebar, Sidebar,
Responsive, Responsive,
} from "semantic-ui-react"; } from 'semantic-ui-react';
const NavBarMobile = ({ const NavBarMobile = ({
children, children,
@ -30,7 +30,7 @@ const NavBarMobile = ({
<Sidebar.Pusher <Sidebar.Pusher
dimmed={visible} dimmed={visible}
onClick={onPusherClick} onClick={onPusherClick}
style={{ minHeight: "100vh" }} style={{ minHeight: '100vh' }}
> >
<Menu fixed="top" inverted> <Menu fixed="top" inverted>
<Menu.Item> <Menu.Item>
@ -67,7 +67,7 @@ const NavBarDesktop = ({ leftItems, rightItems }) => (
); );
const NavBarChildren = ({ children }) => ( const NavBarChildren = ({ children }) => (
<Container style={{ marginTop: "5em" }}>{children}</Container> <Container style={{ marginTop: '5em' }}>{children}</Container>
); );
class HomeNavabarApp extends Component { class HomeNavabarApp extends Component {
@ -75,10 +75,11 @@ class HomeNavabarApp extends Component {
super(props); super(props);
this.state = { this.state = {
logged: true, logged: true,
email: "", email: '',
password: "", password: '',
}; };
} }
state = { state = {
visible: false, visible: false,
}; };
@ -117,17 +118,23 @@ class HomeNavabarApp extends Component {
} }
} }
const leftItems = [{ as: "a", content: "Home", key: "home", href: "/" }]; const leftItems = [{
as: 'a', content: 'Home', key: 'home', href: '/',
}];
const rightItems = [ const rightItems = [
{ as: "a", content: "Login", key: "login", href: "/login" }, {
{ as: "a", content: "Sign up", key: "register", href: "/signup" }, as: 'a', content: 'Login', key: 'login', href: '/login',
},
{
as: 'a', content: 'Sign up', key: 'register', href: '/signup',
},
]; ];
const HomeNavbarApp = () => ( const HomeNavbarApp = () => (
<HomeNavabarApp <HomeNavabarApp
leftItems={leftItems} leftItems={leftItems}
rightItems={rightItems} rightItems={rightItems}
></HomeNavabarApp> />
); );
export default HomeNavbarApp; export default HomeNavbarApp;

View file

@ -1,4 +1,4 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import { import {
Button, Button,
Header, Header,
@ -7,10 +7,10 @@ import {
Form, Form,
Responsive, Responsive,
Dropdown, Dropdown,
} from "semantic-ui-react"; } from 'semantic-ui-react';
import { connect } from "react-redux"; import { connect } from 'react-redux';
import { RemoteService, Forms } from "../remote"; import { RemoteService, Forms } from '../remote';
import { appActions } from "../storeActions"; import { appActions } from '../storeActions';
class HostModal extends Component { class HostModal extends Component {
constructor(props) { constructor(props) {
@ -28,8 +28,7 @@ class HostModal extends Component {
.catch(console.error); .catch(console.error);
Forms.fetchAllUsers() Forms.fetchAllUsers()
.then((users) => .then((users) => this.setState({
this.setState({
...this.state, ...this.state,
users: users users: users
.filter((u) => u.id !== this.props.currentUserId) .filter((u) => u.id !== this.props.currentUserId)
@ -38,8 +37,7 @@ class HostModal extends Component {
text: `@${u.username} (${u.name})`, text: `@${u.username} (${u.name})`,
value: u.id, value: u.id,
})), })),
}) }))
)
.catch(console.error); .catch(console.error);
this.saveGuestSettings = this.saveGuestSettings.bind(this); this.saveGuestSettings = this.saveGuestSettings.bind(this);
@ -70,7 +68,7 @@ class HostModal extends Component {
render() { render() {
return ( return (
<React.Fragment> <>
<Responsive minWidth={768}> <Responsive minWidth={768}>
<Button icon labelPosition="left" inverted onClick={this.openModal}> <Button icon labelPosition="left" inverted onClick={this.openModal}>
<Icon name="plus" size="small" /> <Icon name="plus" size="small" />
@ -88,7 +86,7 @@ class HostModal extends Component {
<Header>Select guests</Header> <Header>Select guests</Header>
<Modal.Content> <Modal.Content>
<Form> <Form>
<Form.Field style={{ marginTop: "1rem" }}> <Form.Field style={{ marginTop: '1rem' }}>
<label>Select which users are your guests: </label> <label>Select which users are your guests: </label>
<Dropdown <Dropdown
name="guests" name="guests"
@ -104,22 +102,24 @@ class HostModal extends Component {
</Modal.Content> </Modal.Content>
<Modal.Actions> <Modal.Actions>
<Button color="red" onClick={this.closeModal}> <Button color="red" onClick={this.closeModal}>
<Icon name="remove" /> Discard changes <Icon name="remove" />
{' '}
Discard changes
</Button> </Button>
<Button color="green" onClick={this.saveGuestSettings}> <Button color="green" onClick={this.saveGuestSettings}>
<Icon name="checkmark" /> Save changes <Icon name="checkmark" />
{' '}
Save changes
</Button> </Button>
</Modal.Actions> </Modal.Actions>
</Modal> </Modal>
</React.Fragment> </>
); );
} }
} }
const setActiveHost = (activeHost) => { const setActiveHost = (activeHost) => (dispatch) => dispatch(appActions.setActiveHost(activeHost));
return (dispatch) => dispatch(appActions.setActiveHost(activeHost));
};
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
guests: state.guests, guests: state.guests,
@ -129,6 +129,6 @@ const HostModalContainer = connect(
mapStateToProps, mapStateToProps,
{ ...RemoteService, setActiveHost }, { ...RemoteService, setActiveHost },
null, null,
{ forwardRef: true } { forwardRef: true },
)(HostModal); )(HostModal);
export default HostModalContainer; export default HostModalContainer;

View file

@ -1,4 +1,4 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import { import {
Button, Button,
Header, Header,
@ -9,13 +9,13 @@ import {
Responsive, Responsive,
Image, Image,
Confirm, Confirm,
} from "semantic-ui-react"; } from 'semantic-ui-react';
import SelectIcons from "./SelectIcons"; import { connect } from 'react-redux';
import { connect } from "react-redux"; import SelectIcons from './SelectIcons';
import { RemoteService } from "../remote"; import { RemoteService } from '../remote';
import { appActions } from "../storeActions"; import { appActions } from '../storeActions';
const NO_IMAGE = "https://react.semantic-ui.com/images/wireframe/image.png"; const NO_IMAGE = 'https://react.semantic-ui.com/images/wireframe/image.png';
class RoomModal extends Component { class RoomModal extends Component {
constructor(props) { constructor(props) {
@ -31,9 +31,9 @@ class RoomModal extends Component {
get initialState() { get initialState() {
return { return {
selectedIcon: this.type === "new" ? "home" : this.props.room.icon, selectedIcon: this.type === 'new' ? 'home' : this.props.room.icon,
name: this.type === "new" ? "New Room" : this.props.room.name, name: this.type === 'new' ? 'New Room' : this.props.room.name,
img: this.type === "new" ? null : this.props.room.image, img: this.type === 'new' ? null : this.props.room.image,
openModal: false, openModal: false,
sure: false, sure: false,
}; };
@ -41,7 +41,7 @@ class RoomModal extends Component {
unsetImage = (e) => { unsetImage = (e) => {
e.preventDefault(); e.preventDefault();
this.setState({ ...this.state, img: "" }); this.setState({ ...this.state, img: '' });
}; };
setInitialState() { setInitialState() {
@ -49,11 +49,11 @@ class RoomModal extends Component {
} }
get type() { get type() {
return !this.props.id ? "new" : "modify"; return !this.props.id ? 'new' : 'modify';
} }
addRoomModal = (e) => { addRoomModal = (e) => {
let data = { const data = {
icon: this.state.selectedIcon, icon: this.state.selectedIcon,
name: this.state.name, name: this.state.name,
image: this.state.img, image: this.state.img,
@ -65,17 +65,17 @@ class RoomModal extends Component {
this.setInitialState(); this.setInitialState();
this.closeModal(); this.closeModal();
}) })
.catch((err) => console.error("error in creating room", err)); .catch((err) => console.error('error in creating room', err));
}; };
modifyRoomModal = (e) => { modifyRoomModal = (e) => {
let data = { const data = {
icon: this.state.selectedIcon, icon: this.state.selectedIcon,
name: this.state.name, name: this.state.name,
image: this.state.img, image: this.state.img,
}; };
console.log("data", data); console.log('data', data);
this.props this.props
.saveRoom(data, this.props.id) .saveRoom(data, this.props.id)
@ -83,14 +83,14 @@ class RoomModal extends Component {
this.setInitialState(); this.setInitialState();
this.closeModal(); this.closeModal();
}) })
.catch((err) => console.error("error in updating room", err)); .catch((err) => console.error('error in updating room', err));
}; };
deleteRoom = (e) => { deleteRoom = (e) => {
this.props this.props
.deleteRoom(this.props.id) .deleteRoom(this.props.id)
.then(() => this.closeModal()) .then(() => this.closeModal())
.catch((err) => console.error("error in deleting room", err)); .catch((err) => console.error('error in deleting room', err));
}; };
setSureTrue = () => { setSureTrue = () => {
@ -102,8 +102,8 @@ class RoomModal extends Component {
}; };
changeSomething = (event) => { changeSomething = (event) => {
let nam = event.target.name; const nam = event.target.name;
let val = event.target.value; const val = event.target.value;
this.setState({ [nam]: val }); this.setState({ [nam]: val });
}; };
@ -120,7 +120,7 @@ class RoomModal extends Component {
} }
getBase64(file, callback) { getBase64(file, callback) {
let reader = new FileReader(); const reader = new FileReader();
reader.readAsDataURL(file.target.files[0]); reader.readAsDataURL(file.target.files[0]);
reader.onload = () => { reader.onload = () => {
this.setState(Object.assign(this.state, { img: reader.result })); this.setState(Object.assign(this.state, { img: reader.result }));
@ -130,16 +130,16 @@ class RoomModal extends Component {
render() { render() {
const spaceDiv = { const spaceDiv = {
background: "#f4f4f4", background: '#f4f4f4',
padding: "10px 10px", padding: '10px 10px',
margin: "10px 0px", margin: '10px 0px',
}; };
return ( return (
<div> <div>
{!this.props.nicolaStop ? ( {!this.props.nicolaStop ? (
<div> <div>
<Responsive minWidth={768}> <Responsive minWidth={768}>
{this.type === "new" ? ( {this.type === 'new' ? (
<Button <Button
icon icon
labelPosition="left" labelPosition="left"
@ -154,7 +154,7 @@ class RoomModal extends Component {
)} )}
</Responsive> </Responsive>
<Responsive maxWidth={768}> <Responsive maxWidth={768}>
{this.type === "new" ? ( {this.type === 'new' ? (
<Button <Button
icon icon
fluid fluid
@ -181,7 +181,7 @@ class RoomModal extends Component {
<Modal closeIcon onClose={this.closeModal} open={this.state.openModal}> <Modal closeIcon onClose={this.closeModal} open={this.state.openModal}>
<Header> <Header>
{this.type === "new" ? "Add new room" : "Modify room"} {this.type === 'new' ? 'Add new room' : 'Modify room'}
</Header> </Header>
<Modal.Content> <Modal.Content>
<Form> <Form>
@ -224,12 +224,12 @@ class RoomModal extends Component {
<SelectIcons <SelectIcons
updateIcon={this.updateIcon} updateIcon={this.updateIcon}
currentIcon={ currentIcon={
this.type === "new" ? "home" : this.props.room.icon this.type === 'new' ? 'home' : this.props.room.icon
} }
/> />
</div> </div>
{this.type === "modify" ? ( {this.type === 'modify' ? (
<div> <div>
<Button <Button
icon icon
@ -239,7 +239,8 @@ class RoomModal extends Component {
onClick={this.setSureTrue} onClick={this.setSureTrue}
> >
<Icon name="trash alternate" /> <Icon name="trash alternate" />
Delete Room{" "} Delete Room
{' '}
</Button> </Button>
<Confirm <Confirm
open={this.state.sure} open={this.state.sure}
@ -251,18 +252,20 @@ class RoomModal extends Component {
</Modal.Content> </Modal.Content>
<Modal.Actions> <Modal.Actions>
<Button color="red" onClick={this.closeModal}> <Button color="red" onClick={this.closeModal}>
<Icon name="remove" />{" "} <Icon name="remove" />
{this.type === "new" ? "Cancel" : "Discard changes"} {' '}
{this.type === 'new' ? 'Cancel' : 'Discard changes'}
</Button> </Button>
<Button <Button
color="green" color="green"
onClick={ onClick={
this.type === "new" ? this.addRoomModal : this.modifyRoomModal this.type === 'new' ? this.addRoomModal : this.modifyRoomModal
} }
> >
<Icon name="checkmark" />{" "} <Icon name="checkmark" />
{this.type === "new" ? "Add room" : "Save changes"} {' '}
{this.type === 'new' ? 'Add room' : 'Save changes'}
</Button> </Button>
</Modal.Actions> </Modal.Actions>
</Modal> </Modal>
@ -271,9 +274,7 @@ class RoomModal extends Component {
} }
} }
const setActiveRoom = (activeRoom) => { const setActiveRoom = (activeRoom) => (dispatch) => dispatch(appActions.setActiveRoom(activeRoom));
return (dispatch) => dispatch(appActions.setActiveRoom(activeRoom));
};
const mapStateToProps = (state, ownProps) => ({ const mapStateToProps = (state, ownProps) => ({
room: ownProps.id ? state.rooms[ownProps.id] : null, room: ownProps.id ? state.rooms[ownProps.id] : null,
@ -282,6 +283,6 @@ const RoomModalContainer = connect(
mapStateToProps, mapStateToProps,
{ ...RemoteService, setActiveRoom }, { ...RemoteService, setActiveRoom },
null, null,
{ forwardRef: true } { forwardRef: true },
)(RoomModal); )(RoomModal);
export default RoomModalContainer; export default RoomModalContainer;

View file

@ -1,4 +1,4 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import { import {
Button, Button,
Header, Header,
@ -10,12 +10,12 @@ import {
Dropdown, Dropdown,
Checkbox, Checkbox,
Segment, Segment,
} from "semantic-ui-react"; } from 'semantic-ui-react';
import SelectIcons from "./SelectIcons"; import { connect } from 'react-redux';
import { connect } from "react-redux"; import SelectIcons from './SelectIcons';
import { RemoteService } from "../remote"; import { RemoteService } from '../remote';
import { appActions } from "../storeActions"; import { appActions } from '../storeActions';
//import { update } from "immutability-helper"; // import { update } from "immutability-helper";
class SceneModal extends Component { class SceneModal extends Component {
constructor(props) { constructor(props) {
@ -39,13 +39,13 @@ class SceneModal extends Component {
get initialState() { get initialState() {
return { return {
name: this.type === "new" ? "New Scene" : this.props.scene.name, name: this.type === 'new' ? 'New Scene' : this.props.scene.name,
openModal: false, openModal: false,
selectedIcon: "home", selectedIcon: 'home',
scenes: this.scenes, scenes: this.scenes,
copyFrom: null, copyFrom: null,
guestAccessEnabled: guestAccessEnabled:
this.type === "new" ? null : this.props.scene.guestAccessEnabled, this.type === 'new' ? null : this.props.scene.guestAccessEnabled,
}; };
} }
@ -62,11 +62,11 @@ class SceneModal extends Component {
} }
get type() { get type() {
return !this.props.id ? "new" : "modify"; return !this.props.id ? 'new' : 'modify';
} }
addSceneModal = (e) => { addSceneModal = (e) => {
let data = { const data = {
name: this.state.name, name: this.state.name,
icon: this.state.selectedIcon, icon: this.state.selectedIcon,
}; };
@ -77,11 +77,11 @@ class SceneModal extends Component {
this.setInitialState(); this.setInitialState();
this.closeModal(); this.closeModal();
}) })
.catch((err) => console.error("error in creating room", err)); .catch((err) => console.error('error in creating room', err));
}; };
modifySceneModal = (e) => { modifySceneModal = (e) => {
let data = { const data = {
name: this.state.name, name: this.state.name,
icon: this.state.selectedIcon, icon: this.state.selectedIcon,
guestAccessEnabled: this.state.guestAccessEnabled, guestAccessEnabled: this.state.guestAccessEnabled,
@ -94,19 +94,19 @@ class SceneModal extends Component {
this.setInitialState(); this.setInitialState();
this.closeModal(); this.closeModal();
}) })
.catch((err) => console.error("error in updating room", err)); .catch((err) => console.error('error in updating room', err));
}; };
deleteScene = (e) => { deleteScene = (e) => {
this.props this.props
.deleteScene(this.props.id) .deleteScene(this.props.id)
.then(() => this.closeModal()) .then(() => this.closeModal())
.catch((err) => console.error("error in deleting room", err)); .catch((err) => console.error('error in deleting room', err));
}; };
changeSomething = (event) => { changeSomething = (event) => {
let nam = event.target.name; const nam = event.target.name;
let val = event.target.value; const val = event.target.value;
this.setState({ [nam]: val }); this.setState({ [nam]: val });
}; };
@ -133,16 +133,16 @@ class SceneModal extends Component {
render() { render() {
const spaceDiv = { const spaceDiv = {
background: "#f4f4f4", background: '#f4f4f4',
padding: "10px 10px", padding: '10px 10px',
margin: "10px 0px", margin: '10px 0px',
}; };
return ( return (
<div> <div>
{!this.props.nicolaStop ? ( {!this.props.nicolaStop ? (
<div> <div>
<Responsive minWidth={768}> <Responsive minWidth={768}>
{this.type === "new" ? ( {this.type === 'new' ? (
<Button <Button
icon icon
labelPosition="left" labelPosition="left"
@ -157,7 +157,7 @@ class SceneModal extends Component {
)} )}
</Responsive> </Responsive>
<Responsive maxWidth={768}> <Responsive maxWidth={768}>
{this.type === "new" ? ( {this.type === 'new' ? (
<Button <Button
icon icon
fluid fluid
@ -184,7 +184,7 @@ class SceneModal extends Component {
<Modal closeIcon onClose={this.closeModal} open={this.state.openModal}> <Modal closeIcon onClose={this.closeModal} open={this.state.openModal}>
<Header> <Header>
{this.type === "new" ? "Add new scene" : "Modify scene"} {this.type === 'new' ? 'Add new scene' : 'Modify scene'}
</Header> </Header>
<Modal.Content> <Modal.Content>
<Form> <Form>
@ -205,11 +205,11 @@ class SceneModal extends Component {
<SelectIcons <SelectIcons
updateIcon={this.updateIcon} updateIcon={this.updateIcon}
currentIcon={ currentIcon={
this.type === "new" ? "home" : this.props.scene.icon this.type === 'new' ? 'home' : this.props.scene.icon
} }
/> />
</div> </div>
{this.type === "new" && ( {this.type === 'new' && (
<Form.Field> <Form.Field>
<label>Copy configuration from:</label> <label>Copy configuration from:</label>
<Dropdown <Dropdown
@ -222,23 +222,21 @@ class SceneModal extends Component {
/> />
</Form.Field> </Form.Field>
)} )}
{this.type === "modify" ? ( {this.type === 'modify' ? (
<Form.Field> <Form.Field>
<Segment compact style={{ marginBottom: "1rem" }}> <Segment compact style={{ marginBottom: '1rem' }}>
<Checkbox <Checkbox
label="Enable guest access" label="Enable guest access"
checked={this.state.guestAccessEnabled} checked={this.state.guestAccessEnabled}
toggle toggle
onChange={(e, val) => onChange={(e, val) => this.setGuestAccessEnabled(val.checked)}
this.setGuestAccessEnabled(val.checked)
}
/> />
</Segment> </Segment>
</Form.Field> </Form.Field>
) : null} ) : null}
</Form> </Form>
{this.type === "modify" ? ( {this.type === 'modify' ? (
<Button <Button
icon icon
labelPosition="left" labelPosition="left"
@ -253,18 +251,20 @@ class SceneModal extends Component {
</Modal.Content> </Modal.Content>
<Modal.Actions> <Modal.Actions>
<Button color="red" onClick={this.closeModal}> <Button color="red" onClick={this.closeModal}>
<Icon name="remove" />{" "} <Icon name="remove" />
{this.type === "new" ? "Cancel" : "Discard changes"} {' '}
{this.type === 'new' ? 'Cancel' : 'Discard changes'}
</Button> </Button>
<Button <Button
color="green" color="green"
onClick={ onClick={
this.type === "new" ? this.addSceneModal : this.modifySceneModal this.type === 'new' ? this.addSceneModal : this.modifySceneModal
} }
> >
<Icon name="checkmark" />{" "} <Icon name="checkmark" />
{this.type === "new" ? "Add scene" : "Save changes"} {' '}
{this.type === 'new' ? 'Add scene' : 'Save changes'}
</Button> </Button>
</Modal.Actions> </Modal.Actions>
</Modal> </Modal>
@ -273,9 +273,7 @@ class SceneModal extends Component {
} }
} }
const setActiveScene = (activeScene) => { const setActiveScene = (activeScene) => (dispatch) => dispatch(appActions.setActiveScene(activeScene));
return (dispatch) => dispatch(appActions.setActiveScene(activeScene));
};
const mapStateToProps = (state, ownProps) => ({ const mapStateToProps = (state, ownProps) => ({
scene: ownProps.id ? state.scenes[ownProps.id] : null, scene: ownProps.id ? state.scenes[ownProps.id] : null,
@ -285,6 +283,6 @@ const SceneModalContainer = connect(
mapStateToProps, mapStateToProps,
{ ...RemoteService, setActiveScene }, { ...RemoteService, setActiveScene },
null, null,
{ forwardRef: true } { forwardRef: true },
)(SceneModal); )(SceneModal);
export default SceneModalContainer; export default SceneModalContainer;

View file

@ -1,5 +1,5 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import { Button, Grid } from "semantic-ui-react"; import { Button, Grid } from 'semantic-ui-react';
export default class SelectIcons extends Component { export default class SelectIcons extends Component {
constructor(props) { constructor(props) {
@ -11,7 +11,7 @@ export default class SelectIcons extends Component {
selectIcon = (e) => { selectIcon = (e) => {
let el = e.target.name; let el = e.target.name;
if (e.target.tagName === "I") { if (e.target.tagName === 'I') {
el = e.target.parentNode.name; el = e.target.parentNode.name;
} }
this.props.updateIcon(el); this.props.updateIcon(el);
@ -20,36 +20,32 @@ export default class SelectIcons extends Component {
render() { render() {
const myicons = [ const myicons = [
["home", "coffee", "beer", "glass martini", "film", "video"], ['home', 'coffee', 'beer', 'glass martini', 'film', 'video'],
["music", "headphones", "fax", "phone", "laptop", "bath"], ['music', 'headphones', 'fax', 'phone', 'laptop', 'bath'],
["shower", "bed", "child", "warehouse", "car", "bicycle"], ['shower', 'bed', 'child', 'warehouse', 'car', 'bicycle'],
["motorcycle", "archive", "boxes", "cubes", "chess", "gamepad"], ['motorcycle', 'archive', 'boxes', 'cubes', 'chess', 'gamepad'],
["futbol", "table tennis", "server", "tv", "heart", "camera"], ['futbol', 'table tennis', 'server', 'tv', 'heart', 'camera'],
["trophy", "wrench", "image", "book", "university", "medkit"], ['trophy', 'wrench', 'image', 'book', 'university', 'medkit'],
["paw", "tree", "utensils", "male", "female", "life ring outline"], ['paw', 'tree', 'utensils', 'male', 'female', 'life ring outline'],
]; ];
return ( return (
<Grid centered relaxed> <Grid centered relaxed>
{myicons.map((e, i) => { {myicons.map((e, i) => (
return (
<Grid.Row key={i}> <Grid.Row key={i}>
{e.map((e, i) => { {e.map((e, i) => (
return (
<Grid.Column key={i}> <Grid.Column key={i}>
<Button <Button
name={e} name={e}
color={e === this.state.currentIcon ? "black" : null} color={e === this.state.currentIcon ? 'black' : null}
icon={e} icon={e}
size="small" size="small"
onClick={this.selectIcon} onClick={this.selectIcon}
/> />
</Grid.Column> </Grid.Column>
); ))}
})}
</Grid.Row> </Grid.Row>
); ))}
})}
</Grid> </Grid>
); );
} }

View file

@ -1,12 +1,5 @@
import React from "react"; import React from 'react';
import Modal from "react-modal"; import Modal from 'react-modal';
import SliderTemperature from "./SliderTemperature.js";
import SliderTempRange from "./SliderTempRange.js";
import SliderHumidty from "./SliderHumidity.js";
import SliderLight from "./SliderLight.js";
import SliderLightRange from "./SliderLightRange.js";
import SliderMotion from "./SliderMotion.js";
import DropdownSimulation from "./DropdownSimulation.js";
import { import {
Grid, Grid,
Divider, Divider,
@ -17,8 +10,15 @@ import {
Image, Image,
GridColumn, GridColumn,
Header, Header,
Segment Segment,
} from "semantic-ui-react"; } from 'semantic-ui-react';
import SliderTemperature from './SliderTemperature.js';
import SliderTempRange from './SliderTempRange.js';
import SliderHumidty from './SliderHumidity.js';
import SliderLight from './SliderLight.js';
import SliderLightRange from './SliderLightRange.js';
import SliderMotion from './SliderMotion.js';
import DropdownSimulation from './DropdownSimulation.js';
const SimulationPanel = (props) => ( const SimulationPanel = (props) => (
@ -34,7 +34,7 @@ const SimulationPanel = (props) => (
left: 0, left: 0,
right: 0, right: 0,
bottom: 0, bottom: 0,
backgroundColor: 'rgba(0,0,0,0.5)' backgroundColor: 'rgba(0,0,0,0.5)',
}, },
content: { content: {
position: 'absolute', position: 'absolute',
@ -49,8 +49,8 @@ const SimulationPanel = (props) => (
borderRadius: '4px', borderRadius: '4px',
outline: 'none', outline: 'none',
padding: '20px', padding: '20px',
backgroundColor: 'rgb(65, 67, 69)' backgroundColor: 'rgb(65, 67, 69)',
} },
}} }}
> >
{props.simulationPanel} {props.simulationPanel}
@ -58,7 +58,7 @@ const SimulationPanel = (props) => (
{/* TITLE */} {/* TITLE */}
<Grid.Row textAlign="center"> <Grid.Row textAlign="center">
<h1 style={{ color: "white", padding:"1rem", fontFamily: "Arial"}}> <h1 style={{ color: 'white', padding: '1rem', fontFamily: 'Arial' }}>
Welcome in the Simulation Panel Welcome in the Simulation Panel
</h1> </h1>
</Grid.Row> </Grid.Row>
@ -68,23 +68,23 @@ const SimulationPanel = (props) => (
<Grid.Column width={6} textAlign="center"> <Grid.Column width={6} textAlign="center">
<Divider <Divider
style={{ style={{
marginTop: "3rem", marginTop: '3rem',
marginBottom: "auto" marginBottom: 'auto',
}} }}
/> />
<Image <Image
src='./../img/thermo-simulation.png' src="./../img/thermo-simulation.png"
style={{ style={{
width: "60px", width: '60px',
height: "auto", height: 'auto',
display:"block", display: 'block',
marginLeft:"auto", marginLeft: 'auto',
marginRight:"auto", marginRight: 'auto',
marginTop: "1rem", marginTop: '1rem',
marginBottom: "1rem" marginBottom: '1rem',
}} }}
/> />
<p style={{ color: "white", padding:"0.5rem"}}> <p style={{ color: 'white', padding: '0.5rem' }}>
Temperature Sensor Temperature Sensor
</p> </p>
<Divider /> <Divider />
@ -95,8 +95,8 @@ const SimulationPanel = (props) => (
<div <div
style={{ style={{
display: "block", display: 'block',
marginLeft: "30%", marginLeft: '30%',
}} }}
> >
<SliderTemperature /> <SliderTemperature />
@ -104,11 +104,11 @@ const SimulationPanel = (props) => (
<Divider /> <Divider />
<div <div
style={{ style={{
display: "block", display: 'block',
marginLeft: "30%", marginLeft: '30%',
}} }}
> >
<SliderTempRange/> <SliderTempRange />
</div> </div>
</Grid.Column> </Grid.Column>
</Grid.Row> </Grid.Row>
@ -118,23 +118,23 @@ const SimulationPanel = (props) => (
<Grid.Column width={6} textAlign="center"> <Grid.Column width={6} textAlign="center">
<Divider <Divider
style={{ style={{
marginTop: "3rem", marginTop: '3rem',
marginBottom: "auto" marginBottom: 'auto',
}} }}
/> />
<Image <Image
src='./../img/humidity-simulation.png' src="./../img/humidity-simulation.png"
style={{ style={{
width: "60px", width: '60px',
height: "auto", height: 'auto',
display:"block", display: 'block',
marginLeft:"auto", marginLeft: 'auto',
marginRight:"auto", marginRight: 'auto',
marginTop: "1rem", marginTop: '1rem',
marginBottom: "1rem" marginBottom: '1rem',
}} }}
/> />
<p style={{ color: "white", padding:"0.5rem"}}> <p style={{ color: 'white', padding: '0.5rem' }}>
Humidity Sensor Humidity Sensor
</p> </p>
<Divider /> <Divider />
@ -145,8 +145,8 @@ const SimulationPanel = (props) => (
<div <div
style={{ style={{
display: "block", display: 'block',
marginLeft: "30%", marginLeft: '30%',
}} }}
> >
<SliderHumidty /> <SliderHumidty />
@ -154,11 +154,11 @@ const SimulationPanel = (props) => (
<Divider /> <Divider />
<div <div
style={{ style={{
display: "block", display: 'block',
marginLeft: "30%", marginLeft: '30%',
}} }}
> >
<SliderTempRange/> <SliderTempRange />
</div> </div>
</Grid.Column> </Grid.Column>
</Grid.Row> </Grid.Row>
@ -168,23 +168,23 @@ const SimulationPanel = (props) => (
<Grid.Column width={6} textAlign="center"> <Grid.Column width={6} textAlign="center">
<Divider <Divider
style={{ style={{
marginTop: "3rem", marginTop: '3rem',
marginBottom: "auto" marginBottom: 'auto',
}} }}
/> />
<Image <Image
src='./../img/light-simulation.png' src="./../img/light-simulation.png"
style={{ style={{
width: "60px", width: '60px',
height: "auto", height: 'auto',
display:"block", display: 'block',
marginLeft:"auto", marginLeft: 'auto',
marginRight:"auto", marginRight: 'auto',
marginTop: "1rem", marginTop: '1rem',
marginBottom: "1rem" marginBottom: '1rem',
}} }}
/> />
<p style={{ color: "white", padding:"0.5rem"}}> <p style={{ color: 'white', padding: '0.5rem' }}>
Light Sensor Light Sensor
</p> </p>
<Divider /> <Divider />
@ -195,8 +195,8 @@ const SimulationPanel = (props) => (
<div <div
style={{ style={{
display: "block", display: 'block',
marginLeft: "30%", marginLeft: '30%',
}} }}
> >
<SliderLight /> <SliderLight />
@ -204,11 +204,11 @@ const SimulationPanel = (props) => (
<Divider /> <Divider />
<div <div
style={{ style={{
display: "block", display: 'block',
marginLeft: "30%", marginLeft: '30%',
}} }}
> >
<SliderLightRange/> <SliderLightRange />
</div> </div>
</Grid.Column> </Grid.Column>
</Grid.Row> </Grid.Row>
@ -218,23 +218,23 @@ const SimulationPanel = (props) => (
<Grid.Column width={6} textAlign="center"> <Grid.Column width={6} textAlign="center">
<Divider <Divider
style={{ style={{
marginTop: "3rem", marginTop: '3rem',
marginBottom: "auto" marginBottom: 'auto',
}} }}
/> />
<Image <Image
src='./../img/motion-simulation.png' src="./../img/motion-simulation.png"
style={{ style={{
width: "60px", width: '60px',
height: "auto", height: 'auto',
display:"block", display: 'block',
marginLeft:"auto", marginLeft: 'auto',
marginRight:"auto", marginRight: 'auto',
marginTop: "1rem", marginTop: '1rem',
marginBottom: "1rem" marginBottom: '1rem',
}} }}
/> />
<p style={{ color: "white", padding:"0.5rem"}}> <p style={{ color: 'white', padding: '0.5rem' }}>
Motion Sensor Motion Sensor
</p> </p>
<Divider /> <Divider />
@ -245,8 +245,8 @@ const SimulationPanel = (props) => (
<div <div
style={{ style={{
display: "block", display: 'block',
marginLeft: "30%", marginLeft: '30%',
}} }}
> >
<SliderMotion /> <SliderMotion />
@ -254,11 +254,11 @@ const SimulationPanel = (props) => (
<Divider /> <Divider />
<div <div
style={{ style={{
display: "block", display: 'block',
marginLeft: "30%", marginLeft: '30%',
}} }}
> >
<SliderTempRange/> <SliderTempRange />
</div> </div>
</Grid.Column> </Grid.Column>
</Grid.Row> </Grid.Row>

View file

@ -1,29 +1,30 @@
import React, { Component } from 'react' import React, { Component } from 'react';
import { Form, Grid, Image, Transition, Divider } from 'semantic-ui-react' import {
Form, Grid, Image, Transition, Divider,
} from 'semantic-ui-react';
export default class SliderHumidity extends Component { export default class SliderHumidity extends Component {
state = {visible: true, duration: 78 } state = { visible: true, duration: 78 }
handleChange = (e, { name, value }) => this.setState({ [name]: value }) handleChange = (e, { name, value }) => this.setState({ [name]: value })
handleVisibility = () => handleVisibility = () => this.setState((prevState) => ({ visible: !prevState.visible }))
this.setState((prevState) => ({ visible: !prevState.visible }))
render() { render() {
const { duration } = this.state const { duration } = this.state;
return ( return (
<Grid columns={2}> <Grid columns={2}>
<Grid.Column as={Form} textAlign="center"> <Grid.Column as={Form} textAlign="center">
<p <p
style={{ style={{
color: "white", color: 'white',
padding:"0.5rem", padding: '0.5rem',
display:"block", display: 'block',
marinLeft:"auto", marinLeft: 'auto',
marginRight:"auto",}} marginRight: 'auto',
}}
> >
{`Humidity: ${duration} %`} {`Humidity: ${duration} %`}
</p> </p>
@ -31,14 +32,14 @@ export default class SliderHumidity extends Component {
<Form.Input <Form.Input
min={0} min={0}
max={100} max={100}
name='duration' name="duration"
onChange={this.handleChange} onChange={this.handleChange}
step={1} step={1}
type='range' type="range"
value={duration} value={duration}
/> />
</Grid.Column> </Grid.Column>
</Grid> </Grid>
) );
} }
} }

View file

@ -1,29 +1,30 @@
import React, { Component } from 'react' import React, { Component } from 'react';
import { Form, Grid, Image, Transition, Divider } from 'semantic-ui-react' import {
Form, Grid, Image, Transition, Divider,
} from 'semantic-ui-react';
export default class SliderLight extends Component { export default class SliderLight extends Component {
state = {visible: true, duration: 10500 } state = { visible: true, duration: 10500 }
handleChange = (e, { name, value }) => this.setState({ [name]: value }) handleChange = (e, { name, value }) => this.setState({ [name]: value })
handleVisibility = () => handleVisibility = () => this.setState((prevState) => ({ visible: !prevState.visible }))
this.setState((prevState) => ({ visible: !prevState.visible }))
render() { render() {
const { duration } = this.state const { duration } = this.state;
return ( return (
<Grid columns={2}> <Grid columns={2}>
<Grid.Column as={Form} textAlign="center"> <Grid.Column as={Form} textAlign="center">
<p <p
style={{ style={{
color: "white", color: 'white',
padding:"0.5rem", padding: '0.5rem',
display:"block", display: 'block',
marinLeft:"auto", marinLeft: 'auto',
marginRight:"auto",}} marginRight: 'auto',
}}
> >
{`Light intensity: ${duration} lm`} {`Light intensity: ${duration} lm`}
</p> </p>
@ -31,14 +32,14 @@ export default class SliderLight extends Component {
<Form.Input <Form.Input
min={0} min={0}
max={15000} max={15000}
name='duration' name="duration"
onChange={this.handleChange} onChange={this.handleChange}
step={5} step={5}
type='range' type="range"
value={duration} value={duration}
/> />
</Grid.Column> </Grid.Column>
</Grid> </Grid>
) );
} }
} }

View file

@ -1,29 +1,30 @@
import React, { Component } from 'react' import React, { Component } from 'react';
import { Form, Grid, Image, Transition, Divider } from 'semantic-ui-react' import {
Form, Grid, Image, Transition, Divider,
} from 'semantic-ui-react';
export default class SliderTempRange extends Component { export default class SliderTempRange extends Component {
state = {visible: true, duration: 500 } state = { visible: true, duration: 500 }
handleChange = (e, { name, value }) => this.setState({ [name]: value }) handleChange = (e, { name, value }) => this.setState({ [name]: value })
handleVisibility = () => handleVisibility = () => this.setState((prevState) => ({ visible: !prevState.visible }))
this.setState((prevState) => ({ visible: !prevState.visible }))
render() { render() {
const { duration } = this.state const { duration } = this.state;
return ( return (
<Grid columns={2}> <Grid columns={2}>
<Grid.Column as={Form} textAlign="center"> <Grid.Column as={Form} textAlign="center">
<p <p
style={{ style={{
color: "white", color: 'white',
padding:"0.5rem", padding: '0.5rem',
display:"block", display: 'block',
marinLeft:"auto", marinLeft: 'auto',
marginRight:"auto",}} marginRight: 'auto',
}}
> >
{`Chosen tolerance: +/- ${duration}`} {`Chosen tolerance: +/- ${duration}`}
</p> </p>
@ -32,14 +33,14 @@ export default class SliderTempRange extends Component {
<Form.Input <Form.Input
min={0} min={0}
max={1000} max={1000}
name='duration' name="duration"
onChange={this.handleChange} onChange={this.handleChange}
step={1} step={1}
type='range' type="range"
value={duration} value={duration}
/> />
</Grid.Column> </Grid.Column>
</Grid> </Grid>
) );
} }
} }

View file

@ -1,29 +1,30 @@
import React, { Component } from 'react' import React, { Component } from 'react';
import { Form, Grid, Image, Transition, Divider } from 'semantic-ui-react' import {
Form, Grid, Image, Transition, Divider,
} from 'semantic-ui-react';
export default class SliderLight extends Component { export default class SliderLight extends Component {
state = {visible: true, duration:5 } state = { visible: true, duration: 5 }
handleChange = (e, { name, value }) => this.setState({ [name]: value }) handleChange = (e, { name, value }) => this.setState({ [name]: value })
handleVisibility = () => handleVisibility = () => this.setState((prevState) => ({ visible: !prevState.visible }))
this.setState((prevState) => ({ visible: !prevState.visible }))
render() { render() {
const { duration } = this.state const { duration } = this.state;
return ( return (
<Grid columns={2}> <Grid columns={2}>
<Grid.Column as={Form} textAlign="center"> <Grid.Column as={Form} textAlign="center">
<p <p
style={{ style={{
color: "white", color: 'white',
padding:"0.5rem", padding: '0.5rem',
display:"block", display: 'block',
marinLeft:"auto", marinLeft: 'auto',
marginRight:"auto",}} marginRight: 'auto',
}}
> >
{`Range: ${duration} meters`} {`Range: ${duration} meters`}
</p> </p>
@ -31,14 +32,14 @@ export default class SliderLight extends Component {
<Form.Input <Form.Input
min={0} min={0}
max={15} max={15}
name='duration' name="duration"
onChange={this.handleChange} onChange={this.handleChange}
step={5} step={5}
type='range' type="range"
value={duration} value={duration}
/> />
</Grid.Column> </Grid.Column>
</Grid> </Grid>
) );
} }
} }

View file

@ -1,29 +1,30 @@
import React, { Component } from 'react' import React, { Component } from 'react';
import { Form, Grid, Image, Transition, Divider } from 'semantic-ui-react' import {
Form, Grid, Image, Transition, Divider,
} from 'semantic-ui-react';
export default class SliderTempRange extends Component { export default class SliderTempRange extends Component {
state = {visible: true, duration: 5 } state = { visible: true, duration: 5 }
handleChange = (e, { name, value }) => this.setState({ [name]: value }) handleChange = (e, { name, value }) => this.setState({ [name]: value })
handleVisibility = () => handleVisibility = () => this.setState((prevState) => ({ visible: !prevState.visible }))
this.setState((prevState) => ({ visible: !prevState.visible }))
render() { render() {
const { duration } = this.state const { duration } = this.state;
return ( return (
<Grid columns={2}> <Grid columns={2}>
<Grid.Column as={Form} textAlign="center"> <Grid.Column as={Form} textAlign="center">
<p <p
style={{ style={{
color: "white", color: 'white',
padding:"0.5rem", padding: '0.5rem',
display:"block", display: 'block',
marinLeft:"auto", marinLeft: 'auto',
marginRight:"auto",}} marginRight: 'auto',
}}
> >
{`Chosen tolerance: +/- ${duration}`} {`Chosen tolerance: +/- ${duration}`}
</p> </p>
@ -32,14 +33,14 @@ export default class SliderTempRange extends Component {
<Form.Input <Form.Input
min={0} min={0}
max={10} max={10}
name='duration' name="duration"
onChange={this.handleChange} onChange={this.handleChange}
step={1} step={1}
type='range' type="range"
value={duration} value={duration}
/> />
</Grid.Column> </Grid.Column>
</Grid> </Grid>
) );
} }
} }

View file

@ -1,29 +1,30 @@
import React, { Component } from 'react' import React, { Component } from 'react';
import { Form, Grid, Image, Transition, Divider } from 'semantic-ui-react' import {
Form, Grid, Image, Transition, Divider,
} from 'semantic-ui-react';
export default class SliderTemperature extends Component { export default class SliderTemperature extends Component {
state = {visible: true, duration: 20 } state = { visible: true, duration: 20 }
handleChange = (e, { name, value }) => this.setState({ [name]: value }) handleChange = (e, { name, value }) => this.setState({ [name]: value })
handleVisibility = () => handleVisibility = () => this.setState((prevState) => ({ visible: !prevState.visible }))
this.setState((prevState) => ({ visible: !prevState.visible }))
render() { render() {
const { duration } = this.state const { duration } = this.state;
return ( return (
<Grid columns={2}> <Grid columns={2}>
<Grid.Column as={Form} textAlign="center"> <Grid.Column as={Form} textAlign="center">
<p <p
style={{ style={{
color: "white", color: 'white',
padding:"0.5rem", padding: '0.5rem',
display:"block", display: 'block',
marinLeft:"auto", marinLeft: 'auto',
marginRight:"auto",}} marginRight: 'auto',
}}
> >
{`Heat: ${duration} Celsius Degrees`} {`Heat: ${duration} Celsius Degrees`}
</p> </p>
@ -31,14 +32,14 @@ export default class SliderTemperature extends Component {
<Form.Input <Form.Input
min={10} min={10}
max={35} max={35}
name='duration' name="duration"
onChange={this.handleChange} onChange={this.handleChange}
step={1} step={1}
type='range' type="range"
value={duration} value={duration}
/> />
</Grid.Column> </Grid.Column>
</Grid> </Grid>
) );
} }
} }

View file

@ -1,5 +1,5 @@
import React from "react"; import React from 'react';
import DevicePanel from "./dashboard/DevicePanel"; import DevicePanel from './dashboard/DevicePanel';
export default class VideoTest extends React.Component { export default class VideoTest extends React.Component {
render() { render() {

View file

@ -1,8 +1,8 @@
import React, { Component, useState, useRef } from "react"; import React, { Component, useState, useRef } from 'react';
import { connect } from "react-redux"; import { connect } from 'react-redux';
import { RemoteService } from "../../remote"; import { RemoteService } from '../../remote';
import update from "immutability-helper"; import update from 'immutability-helper';
import "./Automations.css"; import './Automations.css';
import { import {
Segment, Segment,
@ -18,66 +18,76 @@ import {
Form, Form,
Dropdown, Dropdown,
Checkbox, Checkbox,
} from "semantic-ui-react"; } from 'semantic-ui-react';
export const operands = [ export const operands = [
{ key: "EQUAL", text: "=", value: "EQUAL" }, { key: 'EQUAL', text: '=', value: 'EQUAL' },
{ {
key: "GREATER_EQUAL", key: 'GREATER_EQUAL',
text: "\u2265", text: '\u2265',
value: "GREATER_EQUAL", value: 'GREATER_EQUAL',
}, },
{ {
key: "GREATER", key: 'GREATER',
text: ">", text: '>',
value: "GREATER", value: 'GREATER',
}, },
{ {
key: "LESS_EQUAL", key: 'LESS_EQUAL',
text: "\u2264", text: '\u2264',
value: "LESS_EQUAL", value: 'LESS_EQUAL',
}, },
{ {
key: "LESS", key: 'LESS',
text: "<", text: '<',
value: "LESS", value: 'LESS',
}, },
]; ];
const deviceStateOptions = [ const deviceStateOptions = [
{ key: "off", text: "off", value: false }, { key: 'off', text: 'off', value: false },
{ key: "on", text: "on", value: true }, { key: 'on', text: 'on', value: true },
];
const thermostatOptions = [
{ key: 'HEATING', text: 'HEATING', value: 'HEATING' },
{ key: 'COOLING', text: 'COOLING', value: 'COOLING' },
{ key: 'IDLE', text: 'IDLE', value: 'IDLE' },
{ key: 'OFF', text: 'OFF', value: 'OFF' },
];
const thermostatOperands = [
{ key: 'EQUAL', text: '=', value: 'EQUAL' },
{ key: 'NOTEQUAL', text: '\u2260', value: 'NOTEQUAL' },
]; ];
const CreateTrigger = (props) => { const CreateTrigger = (props) => {
const [activeOperand, setActiveOperand] = useState(true); const [activeOperand, setActiveOperand] = useState(true);
const [activeThermostat, setActiveThermostat] = useState(false);
const operandsRef = useRef(null); const operandsRef = useRef(null);
const valuesRef = useRef(null); const valuesRef = useRef(null);
const notAdmitedDevices = ["buttonDimmer"]; const notAdmitedDevices = ['buttonDimmer'];
const hasOperand = new Set([ const hasOperand = new Set([
"knobDimmer", 'knobDimmer',
"dimmableLight", 'dimmableLight',
"curtains", 'curtains',
"sensor", 'sensor',
]); ]);
const deviceList = Object.values(props.devices) const deviceList = Object.values(props.devices)
.map((device) => { .map((device) => ({
return {
key: device.id, key: device.id,
text: device.name, text: device.name,
value: device.id, value: device.id,
kind: device.kind, kind: device.kind,
}; }))
})
.filter((e) => !notAdmitedDevices.includes(e.kind)); .filter((e) => !notAdmitedDevices.includes(e.kind));
const onChange = (e, val) => { const onChange = (e, val) => {
props.inputChange(val); props.inputChange(val);
setActiveOperand(hasOperand.has(props.devices[val.value].kind)); setActiveOperand(hasOperand.has(props.devices[val.value].kind));
setActiveThermostat(props.devices[val.value].kind === 'thermostat');
if (operandsRef.current) operandsRef.current.setValue(""); if (operandsRef.current) operandsRef.current.setValue('');
if (valuesRef.current) if (valuesRef.current) valuesRef.current.inputRef.current.valueAsNumber = undefined;
valuesRef.current.inputRef.current.valueAsNumber = undefined;
}; };
return ( return (
@ -95,8 +105,33 @@ const CreateTrigger = (props) => {
placeholder="Device" placeholder="Device"
/> />
</Form.Field> </Form.Field>
{activeOperand ? ( {
<React.Fragment> activeThermostat ? (
<>
<Form.Field inline width={2}>
<Dropdown
onChange={(e, val) => props.inputChange(val)}
ref={operandsRef}
name="operand"
compact
selection
options={thermostatOperands}
/>
</Form.Field>
<Form.Field inline width={7}>
<Dropdown
onChange={(e, val) => props.inputChange(val)}
placeholder="State"
name="mode"
compact
selection
options={thermostatOptions}
/>
</Form.Field>
</>
)
: activeOperand ? (
<>
<Form.Field inline width={2}> <Form.Field inline width={2}>
<Dropdown <Dropdown
onChange={(e, val) => props.inputChange(val)} onChange={(e, val) => props.inputChange(val)}
@ -118,7 +153,7 @@ const CreateTrigger = (props) => {
placeholder="Value" placeholder="Value"
/> />
</Form.Field> </Form.Field>
</React.Fragment> </>
) : ( ) : (
<Form.Field inline width={7}> <Form.Field inline width={7}>
<Dropdown <Dropdown
@ -130,7 +165,8 @@ const CreateTrigger = (props) => {
options={deviceStateOptions} options={deviceStateOptions}
/> />
</Form.Field> </Form.Field>
)} )
}
</Form.Group> </Form.Group>
</Form> </Form>
</List.Content> </List.Content>
@ -139,7 +175,7 @@ const CreateTrigger = (props) => {
}; };
const SceneItem = (props) => { const SceneItem = (props) => {
let position = props.order.indexOf(props.scene.id); const position = props.order.indexOf(props.scene.id);
return ( return (
<List.Item> <List.Item>
<List.Header> <List.Header>
@ -148,9 +184,7 @@ const SceneItem = (props) => {
<Grid.Column width={4}> <Grid.Column width={4}>
<Checkbox <Checkbox
toggle toggle
onChange={(e, val) => onChange={(e, val) => props.orderScenes(props.scene.id, val.checked)}
props.orderScenes(props.scene.id, val.checked)
}
checked={position + 1 > 0} checked={position + 1 > 0}
/> />
</Grid.Column> </Grid.Column>
@ -158,7 +192,7 @@ const SceneItem = (props) => {
<h3>{props.scene.name}</h3> <h3>{props.scene.name}</h3>
</Grid.Column> </Grid.Column>
<Grid.Column width={4}> <Grid.Column width={4}>
<h3>{position !== -1 ? "# " + (position + 1) : ""}</h3> <h3>{position !== -1 ? `# ${position + 1}` : ''}</h3>
</Grid.Column> </Grid.Column>
</Grid.Row> </Grid.Row>
</Grid> </Grid>
@ -167,8 +201,12 @@ const SceneItem = (props) => {
); );
}; };
const Trigger = ({ deviceName, trigger, onRemove, index }) => { const Trigger = ({
const { operand, value, on } = trigger; deviceName, trigger, onRemove, index,
}) => {
const {
operand, value, on, mode,
} = trigger;
let symbol; let symbol;
if (operand) { if (operand) {
symbol = operands.filter((opt) => opt.key === operand)[0].text; symbol = operands.filter((opt) => opt.key === operand)[0].text;
@ -177,11 +215,11 @@ const Trigger = ({ deviceName, trigger, onRemove, index }) => {
<List.Item className="trigger-item"> <List.Item className="trigger-item">
<Menu compact> <Menu compact>
<Menu.Item as="span">{deviceName}</Menu.Item> <Menu.Item as="span">{deviceName}</Menu.Item>
{operand ? <Menu.Item as="span">{symbol}</Menu.Item> : ""} {operand ? <Menu.Item as="span">{symbol}</Menu.Item> : ''}
<Menu.Item as="span">{operand ? value : on ? "on" : "off"}</Menu.Item> <Menu.Item as="span">{mode || (operand ? value : on ? 'on' : 'off')}</Menu.Item>
</Menu> </Menu>
<Icon <Icon
as={"i"} as="i"
onClick={() => onRemove(index)} onClick={() => onRemove(index)}
className="remove-icon" className="remove-icon"
name="remove" name="remove"
@ -195,10 +233,12 @@ class AutomationSaveModal extends Component {
super(props); super(props);
this.state = { this.state = {
triggerList: [], triggerList: [],
conditionsList: [],
order: [], order: [],
automationName: "New Automation", automationName: 'New Automation',
editName: false, editName: false,
newTrigger: {}, newTrigger: {},
newCondition: {},
scenesFilter: null, scenesFilter: null,
openModal: false, openModal: false,
}; };
@ -210,34 +250,36 @@ class AutomationSaveModal extends Component {
} }
for (const trigger of this.props.automation.triggers) { for (const trigger of this.props.automation.triggers) {
this.state.triggerList.push( this.state.triggerList.push(
Object.assign(
{ {
device: trigger.deviceId, device: trigger.deviceId,
kind: trigger.kind, kind: trigger.kind,
}, ...(trigger.kind === 'booleanTrigger'
trigger.kind === "booleanTrigger"
? { on: trigger.on } ? { on: trigger.on }
: { : {
operand: trigger.operator, operand: trigger.operator,
value: trigger.value, value: trigger.value,
} }),
) },
); );
} }
} }
this.setTrigger = this._setter("triggerList"); this.setTrigger = this._setter('triggerList');
this.setOrder = this._setter("order"); this.setOrder = this._setter('order');
this.setautomationName = this._setter("automationName"); this.setautomationName = this._setter('automationName');
this.setEditName = this._setter("editName"); this.setEditName = this._setter('editName');
this.setNewTrigger = this._setter("newTrigger"); this.setNewTrigger = this._setter('newTrigger');
this.addTrigger = this.addTrigger.bind(this); this.addTrigger = this.addTrigger.bind(this);
this.removeTrigger = this.removeTrigger.bind(this); this.removeTrigger = this.removeTrigger.bind(this);
this.onInputChange = this.onInputChange.bind(this); this.onInputChange = this.onInputChange.bind(this);
this.searchScenes = this.searchScenes.bind(this); this.searchScenes = this.searchScenes.bind(this);
this.orderScenes = this.orderScenes.bind(this); this.orderScenes = this.orderScenes.bind(this);
this.onChangeName = this.onChangeName.bind(this); this.onChangeName = this.onChangeName.bind(this);
// Conditions
this.setNewCondition = this._setter('newCondition');
this.addCondition = this.addCondition.bind(this);
this.removeCondition = this.removeCondition.bind(this);
} }
openModal = (e) => { openModal = (e) => {
@ -253,92 +295,149 @@ class AutomationSaveModal extends Component {
} }
_setter(property) { _setter(property) {
return (value) => return (value) => this.setState(update(this.state, { [property]: { $set: value } }));
this.setState(update(this.state, { [property]: { $set: value } }));
} }
triggerKind(trigger) { triggerKind(trigger) {
if ("operand" in trigger && "value" in trigger) { if ('operand' in trigger && 'value' in trigger) {
return "rangeTrigger"; return 'rangeTrigger';
} else if ("on" in trigger) { }
return "booleanTrigger"; if ('on' in trigger) {
} else { return 'booleanTrigger';
}
return false; return false;
//throw new Error("Trigger kind not handled"); // throw new Error("Trigger kind not handled");
}
} }
_checkNewTrigger(trigger) { conditionKind(condition) {
const error = { if ('operand' in condition && 'value' in condition) {
result: false, return 'rangeTrigger';
message: "There are missing fields!", }
}; if ('on' in condition) {
let device = Object.values(this.props.devices).filter( return 'booleanTrigger';
(d) => d.id === trigger.device
)[0];
let triggerKind = this.triggerKind(trigger);
if (!device || !triggerKind) {
error.message = "There are missing fields";
return error;
} }
let deviceKind = device.kind;
const devicesWithPercentage = ["dimmableLight", "curtains", "knobDimmer"];
switch (triggerKind) { if ('operand' in condition && 'mode' in condition) {
case "booleanTrigger": return 'thermostatCondition';
if (!trigger.device || trigger.on === null || trigger.on === undefined) }
return error; return false;
break; }
case "rangeTrigger":
checkRange(deviceKind, devicesWithPercentage, trigger, error, device) {
if (!trigger.device || !trigger.operand || !trigger.value) { if (!trigger.device || !trigger.operand || !trigger.value) {
return error; return error;
} }
if (trigger.value < 0) { if (trigger.value < 0) {
error.message = "Values cannot be negative"; error.message = 'Values cannot be negative';
return error; return error;
} }
// If the device's range is a percentage, values cannot exceed 100 // If the device's range is a percentage, values cannot exceed 100
else if ( if (
devicesWithPercentage.includes(deviceKind) && devicesWithPercentage.includes(deviceKind)
trigger.value > 100 && trigger.value > 100
) { ) {
error.message = "The value can't exceed 100, as it's a percentage"; error.message = "The value can't exceed 100, as it's a percentage";
return error; return error;
} else if ( }
deviceKind === "sensor" && if (
device.sensor === "HUMIDITY" && deviceKind === 'sensor'
trigger.value > 100 && device.sensor === 'HUMIDITY'
&& trigger.value > 100
) { ) {
error.message = "The value can't exceed 100, as it's a percentage"; error.message = "The value can't exceed 100, as it's a percentage";
return error; return error;
} }
return false;
}
checkBool(trigger, error) {
if (!trigger.device || trigger.on === null || trigger.on === undefined) return error;
return false;
}
_checkNewTrigger(trigger, isCondition = false) {
const error = {
result: false,
message: 'There are missing fields!',
};
const device = Object.values(this.props.devices).filter(
(d) => d.id === trigger.device,
)[0];
const triggerKind = this.triggerKind(trigger);
const conditionKind = this.conditionKind(trigger);
if (!isCondition && (!device || !triggerKind)) {
error.message = 'There are missing fields';
return error;
}
if (isCondition && !conditionKind) {
error.message = 'There are missing fields';
return error;
}
const deviceKind = device.kind;
const devicesWithPercentage = ['dimmableLight', 'curtains', 'knobDimmer'];
switch (isCondition ? conditionKind : triggerKind) {
case 'booleanTrigger':
const checkBoolTrigger = this.checkBool(trigger, error);
if (checkBoolTrigger) {
return checkBoolTrigger;
}
break;
case 'booleanCondition':
const checkBoolCond = this.checkBool(trigger, error);
if (checkBoolCond) {
return checkBoolCond;
}
break;
case 'rangeTrigger':
const checkRangeTrigger = this.checkRange(deviceKind, devicesWithPercentage, trigger, error, device);
if (checkRangeTrigger) {
return checkRangeTrigger;
}
break;
case 'rangeCondition':
const checkRangeCond = this.checkRange(deviceKind, devicesWithPercentage, trigger, error, device);
if (checkRangeCond) {
return checkRangeCond;
}
break;
case 'thermostatCondition':
if (!trigger.device || trigger.mode === null || trigger.mode === undefined || !trigger.operand) return error;
break; break;
default: default:
throw new Error("theoretically unreachable statement"); throw new Error('theoretically unreachable statement');
} }
const isNotDuplicate = !this.state.triggerList.some( let isNotDuplicate = null;
(t) => t.device === trigger.device && t.operand === trigger.operand
);
if (isCondition === true) {
isNotDuplicate = !this.state.conditionsList.some(
(t) => t.device === trigger.device && t.operand === trigger.operand,
);
} else {
isNotDuplicate = !this.state.triggerList.some(
(t) => t.device === trigger.device && t.operand === trigger.operand,
);
}
const type = isCondition ? 'condition' : 'trigger';
const duplicationMessage = `You have already created a ${type} for this device with the same conditions`;
return { return {
result: isNotDuplicate, result: isNotDuplicate,
message: isNotDuplicate message: isNotDuplicate
? null ? null
: "You have already created a trigger for this device with the same conditions", : duplicationMessage,
}; };
} }
addTrigger() { addTrigger() {
const { result, message } = this._checkNewTrigger(this.state.newTrigger); const { result, message } = this._checkNewTrigger(this.state.newTrigger);
if (result) { if (result) {
this.setState( this.setState(
update(this.state, { update(this.state, {
triggerList: { $push: [this.state.newTrigger] }, triggerList: { $push: [this.state.newTrigger] },
}) }),
); );
} else { } else {
alert(message); alert(message);
@ -347,13 +446,13 @@ class AutomationSaveModal extends Component {
removeTrigger(index) { removeTrigger(index) {
this.setState( this.setState(
update(this.state, { triggerList: { $splice: [[index, 1]] } }) update(this.state, { triggerList: { $splice: [[index, 1]] } }),
); );
} }
// This gets triggered when the devices dropdown changes the value. // This gets triggered when the devices dropdown changes the value.
onInputChange(val) { onInputChange(val) {
if (val.name === "device") { if (val.name === 'device') {
this.setNewTrigger({ [val.name]: val.value }); this.setNewTrigger({ [val.name]: val.value });
} else { } else {
this.setNewTrigger({ this.setNewTrigger({
@ -374,7 +473,7 @@ class AutomationSaveModal extends Component {
this.setState( this.setState(
update(this.state, { update(this.state, {
order: (prevList) => prevList.filter((e) => e !== id), order: (prevList) => prevList.filter((e) => e !== id),
}) }),
); );
} }
}; };
@ -387,35 +486,46 @@ class AutomationSaveModal extends Component {
get sceneList() { get sceneList() {
if (!this.scenesFilter) { if (!this.scenesFilter) {
return this.props.scenes; return this.props.scenes;
} else {
return this.props.scenes.filter((e) =>
e.name.includes(this.scenesFilter)
);
} }
return this.props.scenes.filter((e) => e.name.includes(this.scenesFilter));
} }
_generateKey = (trigger) => { generateBoolKey(trigger) {
switch (this.triggerKind(trigger)) { return `${trigger.device}${trigger.on}`;
case "booleanTrigger": }
return "" + trigger.device + trigger.on;
case "rangeTrigger": generateRangeKey(trigger) {
return "" + trigger.device + trigger.operand + trigger.value; return `${trigger.device}${trigger.operand}${trigger.value}`;
}
_generateKey = (trigger, isCondition = false) => {
switch (isCondition ? this.conditionKind(trigger) : this.triggerKind(trigger)) {
case 'booleanTrigger':
return this.generateBoolKey(trigger);
case 'booleanCondition':
return this.generateBoolKey(trigger);
case 'rangeTrigger':
return this.generateRangeKey(trigger);
case 'rangeCondition':
return this.generateRangeKey(trigger);
case 'thermostatCondition':
return `${trigger.device}${trigger.operand}${trigger.mode}`;
default: default:
throw new Error("theoretically unreachable statement"); throw new Error('theoretically unreachable statement');
} }
}; };
checkBeforeSave = () => { checkBeforeSave = () => {
if (!this.state.automationName) { if (!this.state.automationName) {
alert("Give a name to the automation"); alert('Give a name to the automation');
return false; return false;
} }
if (this.state.triggerList.length <= 0) { if (this.state.triggerList.length <= 0) {
alert("You have to create a trigger"); alert('You have to create a trigger');
return false; return false;
} }
if (this.state.order.length <= 0) { if (this.state.order.length <= 0) {
alert("You need at least one active scene"); alert('You need at least one active scene');
return false; return false;
} }
return true; return true;
@ -431,7 +541,7 @@ class AutomationSaveModal extends Component {
automation.id = this.props.id; automation.id = this.props.id;
automation.triggers = []; automation.triggers = [];
automation.scenes = []; automation.scenes = [];
automation.conditions = [];
for (let i = 0; i < this.state.order.length; i++) { for (let i = 0; i < this.state.order.length; i++) {
automation.scenes.push({ automation.scenes.push({
priority: i, priority: i,
@ -442,18 +552,35 @@ class AutomationSaveModal extends Component {
for (const trigger of this.state.triggerList) { for (const trigger of this.state.triggerList) {
const kind = trigger.kind || this.triggerKind(trigger); const kind = trigger.kind || this.triggerKind(trigger);
automation.triggers.push( automation.triggers.push(
Object.assign(
{ {
deviceId: trigger.device, deviceId: trigger.device,
kind, kind,
}, ...(kind === 'booleanTrigger'
kind === "booleanTrigger"
? { on: trigger.on } ? { on: trigger.on }
: { : {
operator: trigger.operand, operator: trigger.operand,
range: parseInt(trigger.value), range: parseInt(trigger.value),
}),
},
);
} }
)
for (const condition of this.state.conditionsList) {
const kind = condition.kind || this.conditionKind(condition);
const loSpagnolo = (kind === 'thermostatCondition' ? { operator: condition.operand, mode: condition.mode }
: {
operator: condition.operand,
range: parseInt(condition.value),
});
automation.conditions.push(
{
deviceId: condition.device,
kind,
...(kind === 'booleanTrigger'
? { on: condition.on }
: loSpagnolo
),
},
); );
} }
@ -467,6 +594,7 @@ class AutomationSaveModal extends Component {
automation, automation,
triggerList: this.state.triggerList, triggerList: this.state.triggerList,
order: this.state.order, order: this.state.order,
conditionList: this.state.conditionsList,
}) })
.then(this.closeModal) .then(this.closeModal)
.catch(console.error); .catch(console.error);
@ -477,7 +605,7 @@ class AutomationSaveModal extends Component {
get trigger() { get trigger() {
return this.props.id ? ( return this.props.id ? (
<Button <Button
style={{ display: "inline" }} style={{ display: 'inline' }}
circular circular
size="small" size="small"
icon="edit" icon="edit"
@ -485,11 +613,45 @@ class AutomationSaveModal extends Component {
/> />
) : ( ) : (
<Button icon labelPosition="left" color="green" onClick={this.openModal}> <Button icon labelPosition="left" color="green" onClick={this.openModal}>
<Icon name="add"></Icon>Create new automation <Icon name="add" />
Create new automation
</Button> </Button>
); );
} }
// CONDITIONS
addCondition() {
// Same method used to check triggers and conditions, not a mistake
const { result, message } = this._checkNewTrigger(this.state.newCondition, true);
if (result) {
this.setState(
update(this.state, {
conditionsList: { $push: [this.state.newCondition] },
}),
);
} else {
alert(message);
}
}
removeCondition(index) {
this.setState(
update(this.state, { conditionsList: { $splice: [[index, 1]] } }),
);
}
onInputChangeCondition = (val) => {
if (val.name === 'device') {
this.setNewCondition({ [val.name]: val.value });
} else {
this.setNewCondition({
...this.state.newCondition,
[val.name]: val.value,
});
}
}
render() { render() {
return ( return (
<Modal <Modal
@ -499,7 +661,7 @@ class AutomationSaveModal extends Component {
onClose={this.closeModal} onClose={this.closeModal}
> >
<Segment basic> <Segment basic>
<Header style={{ display: "inline", marginRight: "1rem" }}> <Header style={{ display: 'inline', marginRight: '1rem' }}>
{this.state.editName ? ( {this.state.editName ? (
<Input <Input
focus focus
@ -511,13 +673,12 @@ class AutomationSaveModal extends Component {
this.state.automationName this.state.automationName
)} )}
</Header> </Header>
<Button <Button
onClick={() => this.setEditName((prev) => !prev)} onClick={() => this.setEditName((prev) => !prev)}
style={{ display: "inline" }} style={{ display: 'inline' }}
circular circular
size="small" size="small"
icon={this.state.editName ? "save" : "edit"} icon={this.state.editName ? 'save' : 'edit'}
/> />
<Segment placeholder className="segment-automations"> <Segment placeholder className="segment-automations">
@ -527,10 +688,10 @@ class AutomationSaveModal extends Component {
<Grid.Column> <Grid.Column>
<Header>Create Triggers</Header> <Header>Create Triggers</Header>
<List divided relaxed> <List divided relaxed>
{this.state.triggerList.length > 0 && {this.state.triggerList.length > 0
this.state.triggerList.map((trigger, i) => { && this.state.triggerList.map((trigger, i) => {
const deviceName = this.deviceList.filter( const deviceName = this.deviceList.filter(
(d) => d.id === trigger.device (d) => d.id === trigger.device,
)[0].name; )[0].name;
const key = this._generateKey(trigger); const key = this._generateKey(trigger);
return ( return (
@ -558,7 +719,7 @@ class AutomationSaveModal extends Component {
</Grid.Column> </Grid.Column>
<Grid.Column> <Grid.Column>
{this.props.scenes.length > 0 ? ( {this.props.scenes.length > 0 ? (
<React.Fragment> <>
<Header>Activate Scenes</Header> <Header>Activate Scenes</Header>
<Input <Input
icon="search" icon="search"
@ -577,24 +738,60 @@ class AutomationSaveModal extends Component {
/> />
))} ))}
</List> </List>
</React.Fragment> </>
) : ( ) : (
<React.Fragment> <>
<Header icon> <Header icon>
<Icon name="world" /> <Icon name="world" />
</Header> </Header>
<Button primary>Create Scene</Button> <Button primary>Create Scene</Button>
</React.Fragment> </>
)} )}
</Grid.Column> </Grid.Column>
</Grid.Row> </Grid.Row>
</Grid> </Grid>
</Segment> </Segment>
<Grid columns={1} stackable textAlign="center">
<Grid.Row verticalAlign="middle">
<Grid.Column>
<Header>Add Conditions</Header>
<List divided relaxed>
{this.state.conditionsList.length > 0
&& this.state.conditionsList.map((condition, i) => {
const deviceName = this.deviceList.filter(
(d) => d.id === condition.device,
)[0].name;
const key = this._generateKey(condition, true);
return (
<Trigger
key={key}
index={i}
deviceName={deviceName}
trigger={condition}
onRemove={this.removeCondition}
/>
);
})}
<CreateTrigger
devices={this.props.devices}
inputChange={this.onInputChangeCondition}
/>
</List>
<Button
onClick={this.addCondition}
circular
icon="add"
color="blue"
size="huge"
/>
</Grid.Column>
</Grid.Row>
</Grid>
<Grid> <Grid>
<Grid.Row> <Grid.Row>
<Grid.Column style={{ marginRight: "1rem" }}> <Grid.Column style={{ marginRight: '1rem' }}>
<Button onClick={() => this.saveAutomation()} color="green"> <Button onClick={() => this.saveAutomation()} color="green">
{this.props.id ? "Save" : "Create"} {this.props.id ? 'Save' : 'Create'}
</Button> </Button>
</Grid.Column> </Grid.Column>
</Grid.Row> </Grid.Row>
@ -612,6 +809,6 @@ const mapStateToProps = (state, ownProps) => ({
}); });
const AutomationSaveModalContainer = connect( const AutomationSaveModalContainer = connect(
mapStateToProps, mapStateToProps,
RemoteService RemoteService,
)(AutomationSaveModal); )(AutomationSaveModal);
export default AutomationSaveModalContainer; export default AutomationSaveModalContainer;

View file

@ -1,7 +1,7 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import { connect } from "react-redux"; import { connect } from 'react-redux';
import { RemoteService } from "../../remote"; import { RemoteService } from '../../remote';
import "./Automations.css"; import './Automations.css';
import { import {
Segment, Segment,
@ -11,52 +11,54 @@ import {
List, List,
Divider, Divider,
Menu, Menu,
} from "semantic-ui-react"; } from 'semantic-ui-react';
import CreateAutomation, { operands } from "./AutomationCreationModal"; import CreateAutomation, { operands } from './AutomationCreationModal';
const Automation = ({ automation, devices, scenes, removeAutomation }) => { const Automation = ({
const { triggers } = automation; automation, devices, scenes, removeAutomation,
}) => {
const { triggers, conditions } = automation;
const scenePriorities = automation.scenes; const scenePriorities = automation.scenes;
const getOperator = (operand) => const getOperator = (operand) => operands.filter((o) => o.key === operand)[0].text;
operands.filter((o) => o.key === operand)[0].text;
return ( return (
<React.Fragment> <>
<Header style={{ display: "inline", marginRight: "1rem" }}> <Header style={{ display: 'inline', marginRight: '1rem' }}>
{automation.name} {automation.name}
</Header> </Header>
<CreateAutomation id={automation.id} /> <CreateAutomation id={automation.id} />
<Button <Button
style={{ display: "inline" }} style={{ display: 'inline' }}
circular circular
onClick={() => removeAutomation(automation.id)} onClick={() => removeAutomation(automation.id)}
color="red" color="red"
size="small" size="small"
icon={"trash alternate outline"} icon="trash alternate outline"
/> />
<Segment placeholder> <Segment placeholder>
<Grid columns={2} stackable textAlign="center"> <Grid columns={2} stackable textAlign="center">
<Divider vertical></Divider> <Divider vertical />
<Grid.Row verticalAlign="middle"> <Grid.Row verticalAlign="middle">
<Grid.Column> <Grid.Column>
<Header>Triggers</Header> <Header>Triggers</Header>
<List divided relaxed> <List divided relaxed>
{triggers !== undefined && {triggers !== undefined
triggers.map((trigger) => { && triggers.map((trigger) => {
const device = devices.filter( const device = devices.filter(
(d) => d.id === trigger.deviceId (d) => d.id === trigger.deviceId,
)[0]; )[0];
return ( return (
<Menu key={trigger.id} compact> <Menu key={trigger.id} compact>
<Menu.Item as="span"> <Menu.Item as="span">
{device.name}{" "} {device.name}
{' '}
{trigger.operator {trigger.operator
? getOperator(trigger.operator) + ? `${getOperator(trigger.operator)
" " + } ${
trigger.range trigger.range}`
: trigger.on : trigger.on
? " - on" ? ' - on'
: " - off"} : ' - off'}
</Menu.Item> </Menu.Item>
</Menu> </Menu>
); );
@ -66,12 +68,12 @@ const Automation = ({ automation, devices, scenes, removeAutomation }) => {
<Grid.Column> <Grid.Column>
<Header>Scenes</Header> <Header>Scenes</Header>
<List divided relaxed> <List divided relaxed>
{scenePriorities !== undefined && {scenePriorities !== undefined
scenePriorities.map((sp) => { && scenePriorities.map((sp) => {
const sceneData = scenes.filter( const sceneData = scenes.filter(
(s) => s.id === sp.sceneId (s) => s.id === sp.sceneId,
)[0]; )[0];
if (!sceneData) return ""; if (!sceneData) return '';
return ( return (
<Menu key={sceneData.id} compact> <Menu key={sceneData.id} compact>
<Menu.Item as="span">{sceneData.name}</Menu.Item> <Menu.Item as="span">{sceneData.name}</Menu.Item>
@ -83,7 +85,37 @@ const Automation = ({ automation, devices, scenes, removeAutomation }) => {
</Grid.Row> </Grid.Row>
</Grid> </Grid>
</Segment> </Segment>
</React.Fragment> <Grid columns={1} stackable textAlign="center">
<Grid.Row verticalAlign="middle">
<Grid.Column>
<Header>Conditions</Header>
<List divided relaxed>
{conditions !== undefined
&& conditions.map((condition) => {
const device = devices.filter(
(d) => d.id === condition.deviceId,
)[0];
return (
<Menu key={condition.id} compact>
<Menu.Item as="span">
{device.name}
{' '}
{condition.operator
? `${getOperator(condition.operator)
} ${
condition.mode ? condition.mode : condition.range}`
: condition.on
? ' - on'
: ' - off'}
</Menu.Item>
</Menu>
);
})}
</List>
</Grid.Column>
</Grid.Row>
</Grid>
</>
); );
}; };
@ -103,13 +135,13 @@ class AutomationsPanel extends Component {
getDevices() { getDevices() {
this.props this.props
.fetchDevices() .fetchDevices()
.catch((err) => console.error(`error fetching devices:`, err)); .catch((err) => console.error('error fetching devices:', err));
} }
getAutomations() { getAutomations() {
this.props this.props
.fetchAutomations() .fetchAutomations()
.catch((err) => console.error(`error fetching automations:`, err)); .catch((err) => console.error('error fetching automations:', err));
} }
removeAutomation = (id) => { removeAutomation = (id) => {
@ -120,7 +152,7 @@ class AutomationsPanel extends Component {
render() { render() {
return ( return (
<Grid style={{ marginTop: "3rem" }}> <Grid style={{ marginTop: '3rem' }}>
<Grid.Row> <Grid.Row>
<Grid.Column textAlign="center" width={16}> <Grid.Column textAlign="center" width={16}>
{!this.state.openModal ? ( {!this.state.openModal ? (
@ -133,9 +165,8 @@ class AutomationsPanel extends Component {
</Grid.Column> </Grid.Column>
</Grid.Row> </Grid.Row>
<Grid.Row> <Grid.Row>
{this.props.automations.map((automation, i) => { {this.props.automations.map((automation, i) => (
return ( <Grid.Column key={i} width={8} style={{ margin: '2rem 0' }}>
<Grid.Column key={i} width={8} style={{ margin: "2rem 0" }}>
<Automation <Automation
removeAutomation={this.removeAutomation} removeAutomation={this.removeAutomation}
scenes={this.props.scenes} scenes={this.props.scenes}
@ -143,8 +174,7 @@ class AutomationsPanel extends Component {
automation={automation} automation={automation}
/> />
</Grid.Column> </Grid.Column>
); ))}
})}
</Grid.Row> </Grid.Row>
</Grid> </Grid>
); );
@ -166,6 +196,6 @@ const mapStateToProps = (state, _) => ({
}); });
const AutomationsPanelContainer = connect( const AutomationsPanelContainer = connect(
mapStateToProps, mapStateToProps,
RemoteService RemoteService,
)(AutomationsPanel); )(AutomationsPanel);
export default AutomationsPanelContainer; export default AutomationsPanelContainer;

View file

@ -1,11 +1,13 @@
// vim: set ts=2 sw=2 et tw=80: // vim: set ts=2 sw=2 et tw=80:
import React, { Component } from "react"; import React, { Component } from 'react';
import { Segment, Card, Header, Icon } from "semantic-ui-react"; import {
import Device from "./devices/Device"; Segment, Card, Header, Icon,
import NewDevice from "./devices/NewDevice"; } from 'semantic-ui-react';
import { connect } from "react-redux"; import { connect } from 'react-redux';
import { RemoteService } from "../../remote"; import Device from './devices/Device';
import NewDevice from './devices/NewDevice';
import { RemoteService } from '../../remote';
class DevicePanel extends Component { class DevicePanel extends Component {
constructor(props) { constructor(props) {
@ -15,35 +17,33 @@ class DevicePanel extends Component {
} }
getDevices() { getDevices() {
if (this.props.tab === "Devices") { if (this.props.tab === 'Devices') {
this.props this.props
.fetchDevices() .fetchDevices()
.catch((err) => console.error(`error fetching devices:`, err)); .catch((err) => console.error('error fetching devices:', err));
} }
} }
render() { render() {
return ( return (
<Card.Group centered style={{ paddingTop: "3rem" }}> <Card.Group centered style={{ paddingTop: '3rem' }}>
{this.props.numbeOfRooms > 0 ? ( {this.props.numbeOfRooms > 0 ? (
<React.Fragment> <>
{this.props.devices.map((e, i) => { {this.props.devices.map((e, i) => <Device key={i} tab={this.props.tab} id={e.id} />)}
return <Device key={i} tab={this.props.tab} id={e.id} />;
})}
{!this.props.isActiveRoomHome ? ( {!this.props.isActiveRoomHome ? (
<Card style={{ height: "27em" }}> <Card style={{ height: '27em' }}>
<Segment basic style={{ width: "100%", height: "100%" }}> <Segment basic style={{ width: '100%', height: '100%' }}>
<NewDevice /> <NewDevice />
</Segment> </Segment>
</Card> </Card>
) : null} ) : null}
</React.Fragment> </>
) : ( ) : (
<Segment placeholder> <Segment placeholder>
<Header icon> <Header icon>
<Icon <Icon
name="exclamation triangle" name="exclamation triangle"
style={{ paddingBottom: "1rem" }} style={{ paddingBottom: '1rem' }}
/> />
Please create a room on the left, and then add devices to the Please create a room on the left, and then add devices to the
same. same.
@ -59,12 +59,11 @@ const mapStateToProps = (state, _) => ({
get devices() { get devices() {
if (state.active.activeRoom === -1) { if (state.active.activeRoom === -1) {
return Object.values(state.devices); return Object.values(state.devices);
} else { }
const deviceArray = [ const deviceArray = [
...state.rooms[state.active.activeRoom].devices, ...state.rooms[state.active.activeRoom].devices,
].sort(); ].sort();
return deviceArray.map((id) => state.devices[id]); return deviceArray.map((id) => state.devices[id]);
}
}, },
get isActiveRoomHome() { get isActiveRoomHome() {
return state.active.activeRoom === -1; return state.active.activeRoom === -1;
@ -76,6 +75,6 @@ const mapStateToProps = (state, _) => ({
}); });
const DevicePanelContainer = connect( const DevicePanelContainer = connect(
mapStateToProps, mapStateToProps,
RemoteService RemoteService,
)(DevicePanel); )(DevicePanel);
export default DevicePanelContainer; export default DevicePanelContainer;

View file

@ -1,14 +1,16 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import { connect } from "react-redux"; import { connect } from 'react-redux';
import { RemoteService } from "../../remote"; import {
import { Card, Segment, Header, Icon, Button } from "semantic-ui-react"; Card, Segment, Header, Icon, Button,
import Device from "../../components/dashboard/devices/Device"; } from 'semantic-ui-react';
import { RemoteService } from '../../remote';
import Device from './devices/Device';
class HostsPanel extends Component { class HostsPanel extends Component {
componentDidUpdate(oldProps) { componentDidUpdate(oldProps) {
if ( if (
oldProps.activeHost !== this.props.activeHost && oldProps.activeHost !== this.props.activeHost
this.props.activeHost !== -1 && this.props.activeHost !== -1
) { ) {
this.props.fetchDevices(null, this.props.activeHost).catch(console.error); this.props.fetchDevices(null, this.props.activeHost).catch(console.error);
this.props.fetchAllRooms(this.props.activeHost).catch(console.error); this.props.fetchAllRooms(this.props.activeHost).catch(console.error);
@ -19,19 +21,19 @@ class HostsPanel extends Component {
applyHostScene(id) { applyHostScene(id) {
this.props this.props
.sceneApply(id, this.props.activeHost) .sceneApply(id, this.props.activeHost)
.then(() => console.log("SCCUESS")) .then(() => console.log('SCCUESS'))
.catch((err) => console.error("sceneApply update error", err)); .catch((err) => console.error('sceneApply update error', err));
} }
render() { render() {
if (this.props.isActiveDefaultHost) { if (this.props.isActiveDefaultHost) {
return ( return (
<Card.Group centered style={{ paddingTop: "3rem" }}> <Card.Group centered style={{ paddingTop: '3rem' }}>
<Segment placeholder> <Segment placeholder>
<Header icon> <Header icon>
<Icon <Icon
name="exclamation triangle" name="exclamation triangle"
style={{ paddingBottom: "1rem" }} style={{ paddingBottom: '1rem' }}
/> />
Please select a host to visit on the left. Please select a host to visit on the left.
</Header> </Header>
@ -41,19 +43,21 @@ class HostsPanel extends Component {
} }
return ( return (
<React.Fragment> <>
<Header style={{ textAlign: "center", marginTop: "3rem" }} as="h3"> <Header style={{ textAlign: 'center', marginTop: '3rem' }} as="h3">
Scenes Scenes
</Header> </Header>
<Card.Group centered> <Card.Group centered>
{this.props.hostScenes.map((scene) => ( {this.props.hostScenes.map((scene) => (
<Card> <Card>
<Card.Header textAlign="center"> <Card.Header textAlign="center">
<Header style={{ margin: "1.5rem 0" }} as="h3"> <Header style={{ margin: '1.5rem 0' }} as="h3">
{scene.name} <Icon name={scene.icon} /> {scene.name}
{' '}
<Icon name={scene.icon} />
</Header> </Header>
</Card.Header> </Card.Header>
<Card.Content extras={true}> <Card.Content extras>
<div className="ui two buttons"> <div className="ui two buttons">
<Button onClick={() => this.applyHostScene(scene.id)}> <Button onClick={() => this.applyHostScene(scene.id)}>
Apply Apply
@ -63,22 +67,20 @@ class HostsPanel extends Component {
</Card> </Card>
))} ))}
</Card.Group> </Card.Group>
<Header style={{ textAlign: "center" }} as="h3"> <Header style={{ textAlign: 'center' }} as="h3">
Devices Devices
</Header> </Header>
<Card.Group centered> <Card.Group centered>
{this.props.hostDeviceIds.map((id) => { {this.props.hostDeviceIds.map((id) => (
return (
<Device <Device
key={id} key={id}
hostId={this.props.activeHost} hostId={this.props.activeHost}
tab="Hosts" tab="Hosts"
id={id} id={id}
/> />
); ))}
})}
</Card.Group> </Card.Group>
</React.Fragment> </>
); );
} }
} }

View file

@ -1,9 +1,11 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import { Button, Modal, Icon, Image, Form, Dropdown } from "semantic-ui-react"; import {
import { connect } from "react-redux"; Button, Modal, Icon, Image, Form, Dropdown,
import { RemoteService } from "../../remote"; } from 'semantic-ui-react';
import styled from "styled-components"; import { connect } from 'react-redux';
//import { appActions } from "../../storeActions"; import styled from 'styled-components';
import { RemoteService } from '../../remote';
// import { appActions } from "../../storeActions";
const StyledDiv = styled.div` const StyledDiv = styled.div`
background-color: #505bda; background-color: #505bda;
@ -31,20 +33,31 @@ class NewSceneDevice extends Component {
this.state = { this.state = {
openModal: false, openModal: false,
sceneDevices: this.props.scene ? this.props.scene.sceneStates : {}, sceneDevices: this.props.scene ? this.props.scene.sceneStates : {},
deviceName: "", deviceName: '',
availableDevices: [],
}; };
this.getDevices(); this.getDevices();
// this.getSceneStates();
this.availableDevices();
// console.log(this.state);
this.setSceneState = this.setSceneState.bind(this); this.setSceneState = this.setSceneState.bind(this);
this.createState = this.createState.bind(this); this.createState = this.createState.bind(this);
this.availableDevices = this.availableDevices.bind(this);
} }
getDevices() { getDevices() {
this.props this.props
.fetchDevices() .fetchDevices()
.catch((err) => console.error(`error fetching devices:`, err)); .catch((err) => console.error('error fetching devices:', err));
} }
// getSceneStates() {
// this.props
// .fetchStates(this.props.activeScene)
// .catch((err) => console.error(`error fetching states`, err));
// }
handleOpen = () => { handleOpen = () => {
this.setState({ openModal: true }); this.setState({ openModal: true });
}; };
@ -53,6 +66,28 @@ class NewSceneDevice extends Component {
this.setState({ openModal: false }); this.setState({ openModal: false });
}; };
availableDevices() {
const availableDevices = [];
this.props.devices.forEach((e) => {
if (
Object.values(this.props.sceneStates).filter((d) => e.id === d.deviceId)
.length < 1
) {
if (e.flowType === 'OUTPUT') {
availableDevices.push({
key: e.id,
text: e.name,
value: e.id,
});
}
} else {
// console.log("NOT FOUND", e);
}
});
this.setState({ availableDevices });
// return availableDevices;
}
resetState = () => { resetState = () => {
this.setState(this.baseState); this.setState(this.baseState);
this.handleClose(); this.handleClose();
@ -64,57 +99,45 @@ class NewSceneDevice extends Component {
createState() { createState() {
for (let i = 0; i < this.state.devicesAttached.length; i++) { for (let i = 0; i < this.state.devicesAttached.length; i++) {
let device = this.props.devices.filter( const device = this.props.devices.filter(
(e) => this.state.devicesAttached[i] === e.id (e) => this.state.devicesAttached[i] === e.id,
); );
let data = { const data = {
sceneId: this.props.activeScene, sceneId: this.props.activeScene,
id: device[0].id, id: device[0].id,
kind: device[0].kind, kind: device[0].kind,
}; };
this.props this.props
.saveState(data) .saveState(data)
.catch((err) => console.error("error in creating state", err)); .catch((err) => console.error('error in creating state', err));
} }
this.resetState(); this.resetState();
} }
render() { render() {
const availableDevices = [];
this.props.devices.forEach((e) => {
if (!Object.keys(this.state.sceneDevices).find((d) => e.id === d)) {
if (e.flowType === "OUTPUT") {
availableDevices.push({
key: e.id,
text: e.name,
value: e.id,
});
}
}
});
return ( return (
<Modal <Modal
closeIcon closeIcon
open={this.state.openModal} open={this.state.openModal}
onClose={this.resetState} onClose={this.resetState}
trigger={ trigger={(
<StyledDiv <StyledDiv
onClick={this.handleOpen} onClick={this.handleOpen}
style={{ style={{
position: "relative", position: 'relative',
top: "calc(50% - 5rem)", top: 'calc(50% - 5rem)',
left: "calc(50% - 5rem)", left: 'calc(50% - 5rem)',
}} }}
> >
<Image src="/img/add.svg" style={{ filter: "invert()" }} /> <Image src="/img/add.svg" style={{ filter: 'invert()' }} />
</StyledDiv> </StyledDiv>
} )}
centered={true} centered
> >
<Modal.Header>Add a New Scene State</Modal.Header> <Modal.Header>Add a New Scene State</Modal.Header>
<Modal.Content> <Modal.Content>
<Form> <Form>
<Form.Field style={{ marginTop: "1rem" }}> <Form.Field style={{ marginTop: '1rem' }}>
<label>Select devices you want to attach: </label> <label>Select devices you want to attach: </label>
<Dropdown <Dropdown
name="scene devices" name="scene devices"
@ -122,7 +145,8 @@ class NewSceneDevice extends Component {
fluid fluid
multiple multiple
onChange={this.setSceneState} onChange={this.setSceneState}
options={availableDevices} onClick={() => this.availableDevices()}
options={this.state.availableDevices}
/> />
</Form.Field> </Form.Field>
</Form> </Form>
@ -145,10 +169,20 @@ class NewSceneDevice extends Component {
const mapStateToProps = (state, _) => ({ const mapStateToProps = (state, _) => ({
devices: Object.values(state.devices), devices: Object.values(state.devices),
get sceneStates() {
if (state.active.activeScene !== -1) {
const stateArray = [
...state.scenes[state.active.activeScene].sceneStates,
].sort();
console.log(state.scenes[state.active.activeScene]);
return stateArray.map((id) => state.sceneStates[id]);
}
return [];
},
activeScene: state.active.activeScene, activeScene: state.active.activeScene,
}); });
const NewSceneDeviceContainer = connect( const NewSceneDeviceContainer = connect(
mapStateToProps, mapStateToProps,
RemoteService RemoteService,
)(NewSceneDevice); )(NewSceneDevice);
export default NewSceneDeviceContainer; export default NewSceneDeviceContainer;

View file

@ -1,9 +1,11 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import { connect } from "react-redux"; import { connect } from 'react-redux';
import { RemoteService } from "../../remote"; import {
import Device from "./devices/Device"; Button, Card, Segment, Header, Icon,
import NewSceneDevice from "./NewSceneDevice"; } from 'semantic-ui-react';
import { Button, Card, Segment, Header, Icon } from "semantic-ui-react"; import { RemoteService } from '../../remote';
import Device from './devices/Device';
import NewSceneDevice from './NewSceneDevice';
class ScenesPanel extends Component { class ScenesPanel extends Component {
constructor(props) { constructor(props) {
@ -15,22 +17,22 @@ class ScenesPanel extends Component {
this.props this.props
.sceneApply(this.props.activeScene) .sceneApply(this.props.activeScene)
.then(() => { .then(() => {
alert("Scene applied."); alert('Scene applied.');
}) })
.catch(console.error); .catch(console.error);
} }
render() { render() {
return ( return (
<Card.Group centered style={{ paddingTop: "3rem" }}> <Card.Group centered style={{ paddingTop: '3rem' }}>
{!this.props.isActiveDefaultScene ? ( {!this.props.isActiveDefaultScene ? (
<Card style={{ height: "27em" }}> <Card style={{ height: '27em' }}>
<Card.Content> <Card.Content>
<Card.Header textAlign="center"> <Card.Header textAlign="center">
<Header as="h3">Add devices - Apply Scene</Header> <Header as="h3">Add devices - Apply Scene</Header>
</Card.Header> </Card.Header>
<Segment basic style={{ width: "100%", height: "100%" }}> <Segment basic style={{ width: '100%', height: '100%' }}>
<NewSceneDevice /> <NewSceneDevice states={this.props.sceneStates} />
</Segment> </Segment>
</Card.Content> </Card.Content>
<Card.Content extra> <Card.Content extra>
@ -46,16 +48,14 @@ class ScenesPanel extends Component {
<Header icon> <Header icon>
<Icon <Icon
name="exclamation triangle" name="exclamation triangle"
style={{ paddingBottom: "1rem" }} style={{ paddingBottom: '1rem' }}
/> />
Please select a scene on the left or add a new one. Please select a scene on the left or add a new one.
</Header> </Header>
</Segment> </Segment>
)} )}
{!this.props.isActiveDefaultScene {!this.props.isActiveDefaultScene
? this.props.sceneStates.map((e, i) => { ? this.props.sceneStates.map((e, i) => <Device key={i} tab={this.props.tab} id={e.id} />)
return <Device key={i} tab={this.props.tab} id={e.id} />;
})
: null} : null}
</Card.Group> </Card.Group>
); );
@ -68,10 +68,10 @@ const mapStateToProps = (state, _) => ({
const stateArray = [ const stateArray = [
...state.scenes[state.active.activeScene].sceneStates, ...state.scenes[state.active.activeScene].sceneStates,
].sort(); ].sort();
console.log(state.scenes[state.active.activeScene]);
return stateArray.map((id) => state.sceneStates[id]); return stateArray.map((id) => state.sceneStates[id]);
} else {
return [];
} }
return [];
}, },
get isActiveDefaultScene() { get isActiveDefaultScene() {
return state.active.activeScene === -1; return state.active.activeScene === -1;
@ -80,6 +80,6 @@ const mapStateToProps = (state, _) => ({
}); });
const ScenesPanelContainer = connect( const ScenesPanelContainer = connect(
mapStateToProps, mapStateToProps,
RemoteService RemoteService,
)(ScenesPanel); )(ScenesPanel);
export default ScenesPanelContainer; export default ScenesPanelContainer;

View file

@ -1,8 +1,8 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import "./Curtains.css"; import './Curtains.css';
import { RemoteService } from "../../../remote"; import { connect } from 'react-redux';
import { connect } from "react-redux"; import { RemoteService } from '../../../remote';
import mapStateToProps from "../../../deviceProps"; import mapStateToProps from '../../../deviceProps';
class Curtain extends Component { class Curtain extends Component {
constructor(props) { constructor(props) {
@ -15,7 +15,7 @@ class Curtain extends Component {
this.setIntensity = this.setIntensity.bind(this); this.setIntensity = this.setIntensity.bind(this);
} }
//getters // getters
get turnedOn() { get turnedOn() {
return this.props.stateOrDevice.on; return this.props.stateOrDevice.on;
} }
@ -26,14 +26,14 @@ class Curtain extends Component {
onClickDevice = () => { onClickDevice = () => {
const on = !this.turnedOn; const on = !this.turnedOn;
if (this.props.tab === "Devices") { if (this.props.tab === 'Devices') {
this.props this.props
.saveDevice({ ...this.props.stateOrDevice, on }) .saveDevice({ ...this.props.stateOrDevice, on })
.catch((err) => console.error("curtains update error", err)); .catch((err) => console.error('curtains update error', err));
} else { } else {
this.props.updateState( this.props.updateState(
{ id: this.props.stateOrDevice.id, on: on }, { id: this.props.stateOrDevice.id, on },
this.props.stateOrDevice.kind this.props.stateOrDevice.kind,
); );
} }
}; };
@ -59,14 +59,14 @@ class Curtain extends Component {
saveIntensity = () => { saveIntensity = () => {
const intensity = Math.round(this.state.intensity); const intensity = Math.round(this.state.intensity);
if (this.props.tab === "Devices") { if (this.props.tab === 'Devices') {
this.props this.props
.saveDevice({ ...this.props.stateOrDevice, intensity }) .saveDevice({ ...this.props.stateOrDevice, intensity })
.catch((err) => console.error("curtain update error", err)); .catch((err) => console.error('curtain update error', err));
} else { } else {
this.props.updateState( this.props.updateState(
{ id: this.props.stateOrDevice.id, intensity: intensity }, { id: this.props.stateOrDevice.id, intensity },
this.props.stateOrDevice.kind this.props.stateOrDevice.kind,
); );
} }
}; };
@ -81,7 +81,7 @@ class Curtain extends Component {
} }
}; };
///*this took me way too much more time than it should have*/ // /*this took me way too much more time than it should have*/
handleChange = (a) => { handleChange = (a) => {
this.setIntensity(a.target.value / 100); this.setIntensity(a.target.value / 100);
@ -94,11 +94,13 @@ class Curtain extends Component {
<div <div
className="open-container" className="open-container"
style={{ style={{
height: (9 * this.props.stateOrDevice.intensity) / 100 + "rem", height: `${(9 * this.props.stateOrDevice.intensity) / 100}rem`,
}} }}
></div>{" "} />
{' '}
<span className="span-open"> <span className="span-open">
{Math.round(this.props.stateOrDevice.intensity)}% {Math.round(this.props.stateOrDevice.intensity)}
%
</span> </span>
<input <input
disabled={this.props.disabled} disabled={this.props.disabled}

View file

@ -1,23 +1,25 @@
import React from "react"; import React from 'react';
import Light from "./Light"; import {
import SmartPlug from "./SmartPlug"; Header, Button, Icon, Card,
import Sensor from "./Sensor"; } from 'semantic-ui-react';
import { ButtonDimmer, KnobDimmer } from "./Dimmer"; import { connect } from 'react-redux';
import Switcher from "./Switch"; import Light from './Light';
import Videocam from "./Videocam"; import SmartPlug from './SmartPlug';
import Curtains from "./Curtain"; import Sensor from './Sensor';
import Thermostat from "./Thermostats"; import { ButtonDimmer, KnobDimmer } from './Dimmer';
import { Header, Button, Icon, Card } from "semantic-ui-react"; import Switcher from './Switch';
import { RemoteService } from "../../../remote"; import Videocam from './Videocam';
import { connect } from "react-redux"; import Curtains from './Curtain';
import DeviceSettingsModal from "./DeviceSettingsModal"; import Thermostat from './Thermostats';
import mapStateToProps from "../../../deviceProps"; import { RemoteService } from '../../../remote';
import DeviceSettingsModal from './DeviceSettingsModal';
import mapStateToProps from '../../../deviceProps';
const centerComponent = { const centerComponent = {
marginLeft: "50%", marginLeft: '50%',
transform: "translateX(-50%)", transform: 'translateX(-50%)',
marginTop: "10%", marginTop: '10%',
marginBottom: "10%", marginBottom: '10%',
}; };
class Device extends React.Component { class Device extends React.Component {
@ -31,14 +33,14 @@ class Device extends React.Component {
} }
edit() { edit() {
console.log("editing device with id=" + this.props.id); console.log(`editing device with id=${this.props.id}`);
this.modalRef.current.openModal(); this.modalRef.current.openModal();
} }
resetSmartPlug() { resetSmartPlug() {
this.props this.props
.smartPlugReset(this.props.id) .smartPlugReset(this.props.id)
.catch((err) => console.error(`Smart plug reset error`, err)); .catch((err) => console.error('Smart plug reset error', err));
} }
deleteState() { deleteState() {
@ -71,7 +73,7 @@ class Device extends React.Component {
id: this.props.id, id: this.props.id,
hostId: this.props.hostId, hostId: this.props.hostId,
}, },
"" '',
); );
} }
@ -82,7 +84,7 @@ class Device extends React.Component {
<Icon name="pencil" /> <Icon name="pencil" />
Edit Edit
</Button> </Button>
{this.props.stateOrDevice.kind === "smartPlug" ? ( {this.props.stateOrDevice.kind === 'smartPlug' ? (
<Button <Button
color="orange" color="orange"
icon icon
@ -119,29 +121,29 @@ class Device extends React.Component {
render() { render() {
return ( return (
<Card style={{ height: "27em" }}> <Card style={{ height: '27em' }}>
<Card.Content> <Card.Content>
<Card.Header textAlign="center"> <Card.Header textAlign="center">
<Header as="h3">{this.deviceName}</Header> <Header as="h3">{this.deviceName}</Header>
<Header as="h4" style={{ marginTop: ".5rem" }}> <Header as="h4" style={{ marginTop: '.5rem' }}>
{this.props.roomName} {this.props.roomName}
</Header> </Header>
</Card.Header> </Card.Header>
<Card.Description <Card.Description
style={ style={
this.props.device.kind !== "curtains" ? centerComponent : null this.props.device.kind !== 'curtains' ? centerComponent : null
} }
> >
{this.renderDeviceComponent()} {this.renderDeviceComponent()}
</Card.Description> </Card.Description>
</Card.Content> </Card.Content>
<Card.Content extra> <Card.Content extra>
{this.props.tab === "Devices" {this.props.tab === 'Devices'
? this.deviceDescription() ? this.deviceDescription()
: this.props.tab === "Scenes" && this.stateDescription()} : this.props.tab === 'Scenes' && this.stateDescription()}
</Card.Content> </Card.Content>
{this.props.tab === "Devices" ? ( {this.props.tab === 'Devices' ? (
<DeviceSettingsModal <DeviceSettingsModal
ref={this.modalRef} ref={this.modalRef}
id={this.props.stateOrDevice.id} id={this.props.stateOrDevice.id}

View file

@ -1,25 +1,31 @@
import React, { Component, useState } from "react"; import React, { Component, useState } from 'react';
import { Button, Form, Icon, Header, Modal, Input } from "semantic-ui-react"; import {
import { connect } from "react-redux"; Button, Form, Icon, Header, Modal, Input,
import { RemoteService } from "../../../remote"; } from 'semantic-ui-react';
import { connect } from 'react-redux';
import { RemoteService } from '../../../remote';
const DeleteModal = (props) => ( const DeleteModal = (props) => (
<Modal <Modal
trigger={ trigger={(
<Button icon labelPosition="left" inverted color="red"> <Button icon labelPosition="left" inverted color="red">
<Icon name="trash alternate" /> <Icon name="trash alternate" />
Delete device Delete device
</Button> </Button>
} )}
closeIcon closeIcon
> >
<Header icon="archive" content="Are you sure ?" /> <Header icon="archive" content="Are you sure ?" />
<Modal.Actions> <Modal.Actions>
<Button color="red"> <Button color="red">
<Icon name="remove" /> No <Icon name="remove" />
{' '}
No
</Button> </Button>
<Button onClick={() => props.removeDevice()} color="green"> <Button onClick={() => props.removeDevice()} color="green">
<Icon name="checkmark" /> Yes <Icon name="checkmark" />
{' '}
Yes
</Button> </Button>
</Modal.Actions> </Modal.Actions>
</Modal> </Modal>
@ -31,7 +37,7 @@ const SettingsForm = (props) => {
setValues({ ...values, [name]: value }); setValues({ ...values, [name]: value });
}; };
const [values, setValues] = useState({ name: "" }); const [values, setValues] = useState({ name: '' });
return ( return (
<Form> <Form>
@ -69,7 +75,7 @@ class DeviceSettingsModal extends Component {
this.updateDevice = this.updateDevice.bind(this); this.updateDevice = this.updateDevice.bind(this);
this.deleteDevice = this.deleteDevice.bind(this); this.deleteDevice = this.deleteDevice.bind(this);
//this.useExternalTempSensor = this.useExternalTempSensor.bind(this); // this.useExternalTempSensor = this.useExternalTempSensor.bind(this);
} }
closeModal = (e) => { closeModal = (e) => {
@ -82,18 +88,18 @@ class DeviceSettingsModal extends Component {
updateDevice(values) { updateDevice(values) {
console.log(values, this.external); console.log(values, this.external);
let name = values.name; let { name } = values;
if (values.name.length === 0) { if (values.name.length === 0) {
name = this.props.device.name; name = this.props.device.name;
} }
let data = { const data = {
...this.props.device, ...this.props.device,
name: name, name,
}; };
if (this.props.device.kind === "thermostat") { if (this.props.device.kind === 'thermostat') {
let external = values.external const external = values.external
? values.external ? values.external
: this.props.device.useExternalSensors; : this.props.device.useExternalSensors;
console.log(external); console.log(external);
@ -103,27 +109,24 @@ class DeviceSettingsModal extends Component {
this.props this.props
.saveDevice(data) .saveDevice(data)
.then(() => this.setState({ openModal: false })) .then(() => this.setState({ openModal: false }))
.catch((err) => .catch((err) => console.error(
console.error(
`settings modal for device ${this.props.id} deletion error`, `settings modal for device ${this.props.id} deletion error`,
err err,
) ));
);
} }
deleteDevice() { deleteDevice() {
this.props this.props
.deleteDevice(this.props.device) .deleteDevice(this.props.device)
.then(() => this.setState({ openModal: false })) .then(() => this.setState({ openModal: false }))
.catch((err) => .catch((err) => console.error(
console.error(
`settings modal for device ${this.props.id} deletion error`, `settings modal for device ${this.props.id} deletion error`,
err err,
) ));
);
} }
_editForm = null; _editForm = null;
get editForm() { get editForm() {
this._editForm = this._editForm || ( this._editForm = this._editForm || (
<SettingsForm <SettingsForm
@ -139,7 +142,10 @@ class DeviceSettingsModal extends Component {
render() { render() {
return ( return (
<Modal closeIcon onClose={this.closeModal} open={this.state.openModal}> <Modal closeIcon onClose={this.closeModal} open={this.state.openModal}>
<Modal.Header>Settings of {this.props.device.name}</Modal.Header> <Modal.Header>
Settings of
{this.props.device.name}
</Modal.Header>
<Modal.Content>{this.editForm}</Modal.Content> <Modal.Content>{this.editForm}</Modal.Content>
</Modal> </Modal>
); );
@ -153,6 +159,6 @@ const DeviceSettingsModalContainer = connect(
mapStateToProps, mapStateToProps,
RemoteService, RemoteService,
null, null,
{ forwardRef: true } { forwardRef: true },
)(DeviceSettingsModal); )(DeviceSettingsModal);
export default DeviceSettingsModalContainer; export default DeviceSettingsModalContainer;

View file

@ -5,20 +5,21 @@
A dimmer without state can just increase or decrease the intensity of a light. <-- DefualtDimmer A dimmer without state can just increase or decrease the intensity of a light. <-- DefualtDimmer
The user can change the state of a dimmer through an intuitive UI in SmartHut . The user can change the state of a dimmer through an intuitive UI in SmartHut .
**/ * */
import React, { Component } from "react"; import React, { Component } from 'react';
import { import {
CircularInput, CircularInput,
CircularProgress, CircularProgress,
CircularThumb, CircularThumb,
} from "react-circular-input"; } from 'react-circular-input';
import { connect } from 'react-redux';
import { import {
ButtonDimmerContainer, ButtonDimmerContainer,
MinusPanel, MinusPanel,
PlusPanel, PlusPanel,
ThumbText, ThumbText,
} from "./styleComponents"; } from './styleComponents';
import { import {
CircularThumbStyle, CircularThumbStyle,
KnobDimmerStyle, KnobDimmerStyle,
@ -26,22 +27,21 @@ import {
textStyle, textStyle,
knobIcon, knobIcon,
knobContainer, knobContainer,
} from "./DimmerStyle"; } from './DimmerStyle';
import { RemoteService } from "../../../remote"; import { RemoteService } from '../../../remote';
import { connect } from "react-redux"; import mapStateToProps from '../../../deviceProps';
import mapStateToProps from "../../../deviceProps";
export class ButtonDimmerComponent extends Component { export class ButtonDimmerComponent extends Component {
increaseIntensity = () => { increaseIntensity = () => {
this.props this.props
.buttonDimmerDim(this.props.id, "UP") .buttonDimmerDim(this.props.id, 'UP')
.catch((err) => console.error("button dimmer increase error", err)); .catch((err) => console.error('button dimmer increase error', err));
}; };
decreaseIntensity = () => { decreaseIntensity = () => {
this.props this.props
.buttonDimmerDim(this.props.id, "DOWN") .buttonDimmerDim(this.props.id, 'DOWN')
.catch((err) => console.error("button dimmer decrease error", err)); .catch((err) => console.error('button dimmer decrease error', err));
}; };
render() { render() {
@ -96,7 +96,7 @@ export class KnobDimmerComponent extends Component {
const val = Math.round(this.state.intensity); const val = Math.round(this.state.intensity);
this.props this.props
.knobDimmerDimTo(this.props.id, val) .knobDimmerDimTo(this.props.id, val)
.catch((err) => console.error("knob dimmer set intensity error", err)); .catch((err) => console.error('knob dimmer set intensity error', err));
} }
render() { render() {
@ -104,7 +104,7 @@ export class KnobDimmerComponent extends Component {
<div style={knobContainer}> <div style={knobContainer}>
<CircularInput <CircularInput
style={KnobDimmerStyle} style={KnobDimmerStyle}
value={+(Math.round(this.state.intensity / 100 + "e+2") + "e-2")} value={+(`${Math.round(`${this.state.intensity / 100}e+2`)}e-2`)}
onChange={this.props.disabled ? null : this.setIntensity} onChange={this.props.disabled ? null : this.setIntensity}
> >
<text <text
@ -121,7 +121,7 @@ export class KnobDimmerComponent extends Component {
style={{ ...KnobProgress, opacity: this.state.intensity + 0.1 }} style={{ ...KnobProgress, opacity: this.state.intensity + 0.1 }}
/> />
<CircularThumb style={CircularThumbStyle} /> <CircularThumb style={CircularThumbStyle} />
<ThumbText color={"#1a2849"} /> <ThumbText color="#1a2849" />
</CircularInput> </CircularInput>
<img alt="Knob Icon" style={knobIcon} src="/img/knobDimmer.svg" /> <img alt="Knob Icon" style={knobIcon} src="/img/knobDimmer.svg" />
</div> </div>

View file

@ -1,72 +1,72 @@
export const KnobDimmerStyle = { export const KnobDimmerStyle = {
cursor: "pointer", cursor: 'pointer',
marginTop: "1rem", marginTop: '1rem',
width: "9rem", width: '9rem',
height: "9rem", height: '9rem',
fill: "#1a2849", fill: '#1a2849',
}; };
export const KnobHolder = { export const KnobHolder = {
marginTop: "1rem", marginTop: '1rem',
cursor: "pointer", cursor: 'pointer',
padding: "3rem", padding: '3rem',
backgroundColor: "white", backgroundColor: 'white',
width: "10rem", width: '10rem',
height: "10rem", height: '10rem',
}; };
export const KnobCircularTrack = { export const KnobCircularTrack = {
fill: "white", fill: 'white',
stroke: "#1a2849", stroke: '#1a2849',
}; };
export const KnobIcon = { export const KnobIcon = {
fill: "#1a2849", fill: '#1a2849',
}; };
export const KnobProgress = { export const KnobProgress = {
stroke: "#1a2849", stroke: '#1a2849',
strokeWidth: "3rem", strokeWidth: '3rem',
}; };
export const ValueStyle = { export const ValueStyle = {
pointerEvents: "none", pointerEvents: 'none',
fill: "#1a2849", fill: '#1a2849',
fontSize: "1.3rem", fontSize: '1.3rem',
fontFamily: "Lato", fontFamily: 'Lato',
textAnchor: "middle", textAnchor: 'middle',
userSelect: "none", userSelect: 'none',
}; };
export const CircularThumbStyle = { export const CircularThumbStyle = {
fill: "white", fill: 'white',
stroke: "#1a2849", stroke: '#1a2849',
strokeWidth: ".2rem", strokeWidth: '.2rem',
r: "1.4rem", r: '1.4rem',
}; };
export const textStyle = { export const textStyle = {
position: "absolute", position: 'absolute',
fill: "#1a2849", fill: '#1a2849',
fontSize: "1.5rem", fontSize: '1.5rem',
fontFamily: "Lato", fontFamily: 'Lato',
overflow: "hidden", overflow: 'hidden',
whiteSpace: "nowrap", whiteSpace: 'nowrap',
textOverflow: "ellipsis", textOverflow: 'ellipsis',
userSelect: "none", userSelect: 'none',
}; };
export const knobIcon = { export const knobIcon = {
position: "absolute", position: 'absolute',
left: "50%", left: '50%',
top: "30%", top: '30%',
transform: "translateX(-50%)", transform: 'translateX(-50%)',
width: "2rem", width: '2rem',
height: "2rem", height: '2rem',
}; };
export const knobContainer = { export const knobContainer = {
position: "relative", position: 'relative',
width: "9rem", width: '9rem',
height: "9rem", height: '9rem',
}; };

View file

@ -7,19 +7,20 @@
* Lights have an internal state that can be changed and it must * Lights have an internal state that can be changed and it must
* be shown accordingly in the SmartHut views (house view and room views). * be shown accordingly in the SmartHut views (house view and room views).
*/ */
import React, { Component } from "react"; import React, { Component } from 'react';
import { Image } from 'semantic-ui-react';
import {
CircularInput,
CircularProgress,
CircularThumb,
} from 'react-circular-input';
import { connect } from 'react-redux';
import { import {
iconStyle, iconStyle,
StyledDiv, StyledDiv,
BottomPanel, BottomPanel,
ThumbText, ThumbText,
} from "./styleComponents"; } from './styleComponents';
import { Image } from "semantic-ui-react";
import {
CircularInput,
CircularProgress,
CircularThumb,
} from "react-circular-input";
import { import {
LightDimmerContainer, LightDimmerContainer,
LightDimmerStyle, LightDimmerStyle,
@ -28,10 +29,9 @@ import {
KnobProgress, KnobProgress,
CircularThumbStyle, CircularThumbStyle,
knobIcon, knobIcon,
} from "./LightStyle"; } from './LightStyle';
import { RemoteService } from "../../../remote"; import { RemoteService } from '../../../remote';
import { connect } from "react-redux"; import mapStateToProps from '../../../deviceProps';
import mapStateToProps from "../../../deviceProps";
class Light extends Component { class Light extends Component {
constructor(props) { constructor(props) {
@ -41,8 +41,8 @@ class Light extends Component {
timeout: null, timeout: null,
}; };
this.iconOn = "/img/lightOn.svg"; this.iconOn = '/img/lightOn.svg';
this.iconOff = "/img/lightOff.svg"; this.iconOff = '/img/lightOff.svg';
this.setIntensity = this.setIntensity.bind(this); this.setIntensity = this.setIntensity.bind(this);
} }
@ -68,34 +68,30 @@ class Light extends Component {
onClickDevice = () => { onClickDevice = () => {
const on = !this.turnedOn; const on = !this.turnedOn;
if (this.props.tab !== "Scenes") { if (this.props.tab !== 'Scenes') {
this.props this.props
.saveDevice( .saveDevice(
{ ...this.props.stateOrDevice, on }, { ...this.props.stateOrDevice, on },
this.props.tab === "Hosts" ? this.props.activeHost : null this.props.tab === 'Hosts' ? this.props.activeHost : null,
) )
.catch((err) => console.error("regular light update error", err)); .catch((err) => console.error('regular light update error', err));
} else { } else if (this.props.device.kind === 'regularLight') {
if (this.props.device.kind === "regularLight") {
this.props this.props
.updateState( .updateState(
{ {
id: this.props.stateOrDevice.id, id: this.props.stateOrDevice.id,
on: on, on,
sceneId: this.props.stateOrDevice.sceneId, sceneId: this.props.stateOrDevice.sceneId,
}, },
this.props.stateOrDevice.kind this.props.stateOrDevice.kind,
) )
.then((res) => { .then((res) => {
console.log(res); console.log(res);
}); });
} }
}
}; };
getIcon = () => { getIcon = () => (this.turnedOn ? this.iconOn : this.iconOff);
return this.turnedOn ? this.iconOn : this.iconOff;
};
setIntensity(intensity) { setIntensity(intensity) {
intensity *= 100; intensity *= 100;
@ -118,18 +114,18 @@ class Light extends Component {
saveIntensity = () => { saveIntensity = () => {
const intensity = Math.round(this.state.intensity); const intensity = Math.round(this.state.intensity);
if (this.props.tab !== "Scenes") { if (this.props.tab !== 'Scenes') {
this.props this.props
.saveDevice( .saveDevice(
{ ...this.props.stateOrDevice, intensity }, { ...this.props.stateOrDevice, intensity },
this.props.tab === "Hosts" ? this.props.activeHost : null this.props.tab === 'Hosts' ? this.props.activeHost : null,
) )
.catch((err) => console.error("dimmable light update error", err)); .catch((err) => console.error('dimmable light update error', err));
} else { } else {
this.props this.props
.updateState( .updateState(
{ id: this.props.stateOrDevice.id, intensity: intensity }, { id: this.props.stateOrDevice.id, intensity },
this.props.stateOrDevice.kind this.props.stateOrDevice.kind,
) )
.then((res) => { .then((res) => {
console.log(res, this.props.stateOrDevice.kind); console.log(res, this.props.stateOrDevice.kind);
@ -142,7 +138,7 @@ class Light extends Component {
<div style={LightDimmerContainer}> <div style={LightDimmerContainer}>
<CircularInput <CircularInput
style={LightDimmerStyle} style={LightDimmerStyle}
value={+(Math.round(this.intensity / 100 + "e+2") + "e-2")} value={+(`${Math.round(`${this.intensity / 100}e+2`)}e-2`)}
onChange={this.props.disabled ? null : this.setIntensity} onChange={this.props.disabled ? null : this.setIntensity}
onMouseUp={this.props.disabled ? null : this.saveIntensity} onMouseUp={this.props.disabled ? null : this.saveIntensity}
> >
@ -163,7 +159,7 @@ class Light extends Component {
}} }}
/> />
<CircularThumb style={CircularThumbStyle} /> <CircularThumb style={CircularThumbStyle} />
<ThumbText color={"#ffd31d"} /> <ThumbText color="#ffd31d" />
</CircularInput> </CircularInput>
<Image style={knobIcon} src="/img/intensityLightIcon.svg" /> <Image style={knobIcon} src="/img/intensityLightIcon.svg" />
</div> </div>
@ -173,7 +169,7 @@ class Light extends Component {
<StyledDiv onClick={this.onClickDevice}> <StyledDiv onClick={this.onClickDevice}>
<div> <div>
<Image src={this.getIcon()} style={iconStyle} /> <Image src={this.getIcon()} style={iconStyle} />
<BottomPanel style={{ backgroundColor: "#ffa41b" }}> <BottomPanel style={{ backgroundColor: '#ffa41b' }}>
<h5 style={nameStyle}>Light</h5> <h5 style={nameStyle}>Light</h5>
</BottomPanel> </BottomPanel>
</div> </div>
@ -182,7 +178,7 @@ class Light extends Component {
return ( return (
<div> <div>
{this.props.device.kind === "dimmableLight" {this.props.device.kind === 'dimmableLight'
? intensityLightView ? intensityLightView
: normalLightView} : normalLightView}
</div> </div>

View file

@ -1,69 +1,69 @@
export const valueStyle = { export const valueStyle = {
fill: "#3e99ff", fill: '#3e99ff',
fontSize: "2.5rem", fontSize: '2.5rem',
fontFamily: "Lato", fontFamily: 'Lato',
textShadow: "1px 1px 0.5px rgba(0, 0, 0, .2)", textShadow: '1px 1px 0.5px rgba(0, 0, 0, .2)',
}; };
export const intensityLightStyle = { export const intensityLightStyle = {
fill: "#ffd31d", fill: '#ffd31d',
fontSize: "1.2rem", fontSize: '1.2rem',
fontFamily: "Lato", fontFamily: 'Lato',
textShadow: "1px 1px 0.5px rgba(0, 0, 0, .2)", textShadow: '1px 1px 0.5px rgba(0, 0, 0, .2)',
}; };
export const textStyle = { export const textStyle = {
position: "absolute", position: 'absolute',
fill: "#ffd31d", fill: '#ffd31d',
fontSize: "1.5rem", fontSize: '1.5rem',
fontFamily: "Lato", fontFamily: 'Lato',
overflow: "hidden", overflow: 'hidden',
whiteSpace: "nowrap", whiteSpace: 'nowrap',
textOverflow: "ellipsis", textOverflow: 'ellipsis',
userSelect: "none", userSelect: 'none',
}; };
export const nameStyle = { export const nameStyle = {
fontSize: "1rem", fontSize: '1rem',
position: "absolute", position: 'absolute',
top: "30%", top: '30%',
left: "50%", left: '50%',
transform: "translateX(-50%)", transform: 'translateX(-50%)',
color: "white", color: 'white',
userSelect: "none", userSelect: 'none',
}; };
export const LightDimmerStyle = { export const LightDimmerStyle = {
cursor: "pointer", cursor: 'pointer',
marginTop: "1rem", marginTop: '1rem',
width: "9rem", width: '9rem',
height: "9rem", height: '9rem',
fill: "#ffd31d", fill: '#ffd31d',
}; };
export const LightDimmerContainer = { export const LightDimmerContainer = {
position: "relative", position: 'relative',
width: "9rem", width: '9rem',
height: "9rem", height: '9rem',
}; };
export const CircularThumbStyle = { export const CircularThumbStyle = {
fill: "white", fill: 'white',
stroke: "#ffd31d", stroke: '#ffd31d',
strokeWidth: ".2rem", strokeWidth: '.2rem',
r: "1.4rem", r: '1.4rem',
}; };
export const KnobProgress = { export const KnobProgress = {
stroke: "#ffd31d", stroke: '#ffd31d',
strokeWidth: "3rem", strokeWidth: '3rem',
}; };
export const knobIcon = { export const knobIcon = {
position: "absolute", position: 'absolute',
left: "50%", left: '50%',
top: "30%", top: '30%',
transform: "translateX(-50%)", transform: 'translateX(-50%)',
width: "2rem", width: '2rem',
height: "2rem", height: '2rem',
}; };

View file

@ -1,5 +1,5 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import styled from "styled-components"; import styled from 'styled-components';
import { import {
Button, Button,
Dropdown, Dropdown,
@ -8,9 +8,9 @@ import {
Image, Image,
Input, Input,
Modal, Modal,
} from "semantic-ui-react"; } from 'semantic-ui-react';
import { RemoteService } from "../../../remote"; import { connect } from 'react-redux';
import { connect } from "react-redux"; import { RemoteService } from '../../../remote';
const StyledDiv = styled.div` const StyledDiv = styled.div`
background-color: #505bda; background-color: #505bda;
@ -38,7 +38,7 @@ class NewDevice extends Component {
step: 1, step: 1,
openModal: false, openModal: false,
motion: false, motion: false,
deviceName: "", deviceName: '',
}; };
this.baseState = this.state; this.baseState = this.state;
this.createDevice = this.createDevice.bind(this); this.createDevice = this.createDevice.bind(this);
@ -47,6 +47,7 @@ class NewDevice extends Component {
handleOpen = () => { handleOpen = () => {
this.setState({ openModal: true }); this.setState({ openModal: true });
}; };
handleClose = () => { handleClose = () => {
this.setState({ openModal: false }); this.setState({ openModal: false });
}; };
@ -59,12 +60,13 @@ class NewDevice extends Component {
nextStep = () => { nextStep = () => {
this.setState((prevState) => ({ step: prevState.step + 1 })); this.setState((prevState) => ({ step: prevState.step + 1 }));
}; };
previousStep = () => { previousStep = () => {
this.setState((prevState) => ({ step: prevState.step - 1 })); this.setState((prevState) => ({ step: prevState.step - 1 }));
}; };
setTypeOfDevice = (e, d) => { setTypeOfDevice = (e, d) => {
if (d.value === "dimmableLight") { if (d.value === 'dimmableLight') {
this.setState({ typeOfDevice: d.value, intensity: 0 }); this.setState({ typeOfDevice: d.value, intensity: 0 });
} else { } else {
this.setState({ typeOfDevice: d.value }); this.setState({ typeOfDevice: d.value });
@ -77,7 +79,7 @@ class NewDevice extends Component {
setTypeOfSensor = (e, d) => { setTypeOfSensor = (e, d) => {
console.log(d.value); console.log(d.value);
if (d.value === "motionSensor") { if (d.value === 'motionSensor') {
this.setState({ typeOfSensor: d.value, motion: true }); this.setState({ typeOfSensor: d.value, motion: true });
} else { } else {
this.setState({ typeOfSensor: d.value }); this.setState({ typeOfSensor: d.value });
@ -94,59 +96,59 @@ class NewDevice extends Component {
id: null, id: null,
roomId: this.props.activeRoom, roomId: this.props.activeRoom,
name: this.state.deviceName, name: this.state.deviceName,
kind: this.state.motion ? "motionSensor" : this.state.typeOfDevice, kind: this.state.motion ? 'motionSensor' : this.state.typeOfDevice,
}; };
let outputs = null; let outputs = null;
const defaultNames = { const defaultNames = {
regularLight: "New regular light", regularLight: 'New regular light',
dimmableLight: "New intensity light", dimmableLight: 'New intensity light',
smartPlug: "New smart Plug", smartPlug: 'New smart Plug',
sensor: "New sensor", sensor: 'New sensor',
switch: "New switch", switch: 'New switch',
buttonDimmer: "New button dimmer", buttonDimmer: 'New button dimmer',
knobDimmer: "New knob dimmer", knobDimmer: 'New knob dimmer',
securityCamera: "New security camera", securityCamera: 'New security camera',
thermostat: "New thermostat", thermostat: 'New thermostat',
curtains: "New curtains", curtains: 'New curtains',
}; };
if (this.state.deviceName === "") { if (this.state.deviceName === '') {
data.name = defaultNames[this.state.typeOfDevice]; data.name = defaultNames[this.state.typeOfDevice];
} }
console.log("-------------------------"); console.log('-------------------------');
console.log(this.state.typeOfDevice); console.log(this.state.typeOfDevice);
switch (this.state.typeOfDevice) { switch (this.state.typeOfDevice) {
//trying to make securityCamera work // trying to make securityCamera work
//case "securityCamera": // case "securityCamera":
// data.path="/security_camera_videos/security_camera_1.mp4"; // data.path="/security_camera_videos/security_camera_1.mp4";
// data.on=false; // data.on=false;
//break; // break;
//trying to make thermostat work // trying to make thermostat work
case "thermostat": case 'thermostat':
data.targetTemperature = 0; data.targetTemperature = 0;
data.measuredTemperature = 0; data.measuredTemperature = 0;
break; break;
case "dimmableLight": case 'dimmableLight':
data.intensity = 0; data.intensity = 0;
break; break;
case "sensor": case 'sensor':
if (!this.state.motion) { if (!this.state.motion) {
data.sensor = this.state.typeOfSensor; data.sensor = this.state.typeOfSensor;
data.value = 0; data.value = 0;
} }
break; break;
case "switch": case 'switch':
case "buttonDimmer": case 'buttonDimmer':
case "knobDimmer": case 'knobDimmer':
outputs = this.state.lightsAttached; outputs = this.state.lightsAttached;
if ( if (
this.state.lightsAttached === undefined || this.state.lightsAttached === undefined
this.state.lightsAttached.length === 0 || this.state.lightsAttached.length === 0
) { ) {
alert( alert(
"No lights attached to this switch! Please, add a light a first." 'No lights attached to this switch! Please, add a light a first.',
); );
return; return;
} }
@ -156,114 +158,114 @@ class NewDevice extends Component {
} }
try { try {
let newDevice = await this.props.saveDevice(data); const newDevice = await this.props.saveDevice(data);
if (outputs) { if (outputs) {
await this.props.connectOutputs(newDevice, outputs); await this.props.connectOutputs(newDevice, outputs);
} }
this.resetState(); this.resetState();
} catch (e) { } catch (e) {
console.error("device creation error: ", e); console.error('device creation error: ', e);
} }
} }
render() { render() {
const deviceOptions = [ const deviceOptions = [
//stuff // stuff
{ {
key: "thermostat", key: 'thermostat',
text: "Thermostat", text: 'Thermostat',
value: "thermostat", value: 'thermostat',
image: { avatar: true, src: "/img/thermostat-icon.png" }, image: { avatar: true, src: '/img/thermostat-icon.png' },
}, },
{ {
key: "curtains", key: 'curtains',
text: "Curtain", text: 'Curtain',
value: "curtains", value: 'curtains',
image: { avatar: true, src: "/img/curtains-icon.png" }, image: { avatar: true, src: '/img/curtains-icon.png' },
}, },
//stuff ends // stuff ends
{ {
key: "light", key: 'light',
text: "Normal Light", text: 'Normal Light',
value: "regularLight", value: 'regularLight',
image: { avatar: true, src: "/img/lightOn.svg" }, image: { avatar: true, src: '/img/lightOn.svg' },
}, },
{ {
key: "intensity-light", key: 'intensity-light',
text: "Intensity Light", text: 'Intensity Light',
value: "dimmableLight", value: 'dimmableLight',
image: { avatar: true, src: "/img/intensity-light.svg" }, image: { avatar: true, src: '/img/intensity-light.svg' },
}, },
{ {
key: "smart-plug", key: 'smart-plug',
text: "Smart Plug", text: 'Smart Plug',
value: "smartPlug", value: 'smartPlug',
image: { avatar: true, src: "/img/smart-plug.svg" }, image: { avatar: true, src: '/img/smart-plug.svg' },
}, },
{ {
key: "sensor", key: 'sensor',
text: "Sensor", text: 'Sensor',
value: "sensor", value: 'sensor',
image: { avatar: true, src: "/img/sensorOn.svg" }, image: { avatar: true, src: '/img/sensorOn.svg' },
}, },
{ {
key: "switch", key: 'switch',
text: "Switch", text: 'Switch',
value: "switch", value: 'switch',
image: { avatar: true, src: "/img/switchOn.svg" }, image: { avatar: true, src: '/img/switchOn.svg' },
}, },
{ {
key: "knobDimmer", key: 'knobDimmer',
text: "Knob Dimmer", text: 'Knob Dimmer',
value: "knobDimmer", value: 'knobDimmer',
image: { avatar: true, src: "/img/knob.svg" }, image: { avatar: true, src: '/img/knob.svg' },
}, },
{ {
key: "buttonDimmer", key: 'buttonDimmer',
text: "Button Dimmer", text: 'Button Dimmer',
value: "buttonDimmer", value: 'buttonDimmer',
image: { avatar: true, src: "/img/plusMinus.svg" }, image: { avatar: true, src: '/img/plusMinus.svg' },
}, },
{ {
key: "securityCamera", key: 'securityCamera',
text: "Security Camera", text: 'Security Camera',
value: "securityCamera", value: 'securityCamera',
image: { avatar: true, src: "/img/security-icon.png" }, image: { avatar: true, src: '/img/security-icon.png' },
}, },
]; ];
const sensorOptions = [ const sensorOptions = [
{ {
key: "temperature", key: 'temperature',
text: "Temperature Sensor", text: 'Temperature Sensor',
value: "TEMPERATURE", value: 'TEMPERATURE',
image: { avatar: true, src: "/img/temperature-sensor.svg" }, image: { avatar: true, src: '/img/temperature-sensor.svg' },
}, },
{ {
key: "humidity", key: 'humidity',
text: "Humidity Sensor", text: 'Humidity Sensor',
value: "HUMIDITY", value: 'HUMIDITY',
image: { avatar: true, src: "/img/humidity-sensor.svg" }, image: { avatar: true, src: '/img/humidity-sensor.svg' },
}, },
{ {
key: "light", key: 'light',
text: "Light Sensor", text: 'Light Sensor',
value: "LIGHT", value: 'LIGHT',
image: { avatar: true, src: "/img/light-sensor.svg" }, image: { avatar: true, src: '/img/light-sensor.svg' },
}, },
{ {
key: "motion", key: 'motion',
text: "Motion Sensor", text: 'Motion Sensor',
value: "motionSensor", value: 'motionSensor',
image: { avatar: true, src: "/img/sensorOn.svg" }, image: { avatar: true, src: '/img/sensorOn.svg' },
}, },
]; ];
const availableSwitchDevices = []; const availableSwitchDevices = [];
const availableDimmerDevices = []; const availableDimmerDevices = [];
this.props.devices.forEach((d) => { this.props.devices.forEach((d) => {
if ( if (
d.kind === "regularLight" || d.kind === 'regularLight'
d.kind === "dimmableLight" || || d.kind === 'dimmableLight'
d.kind === "smartPlug" || d.kind === 'smartPlug'
) { ) {
availableSwitchDevices.push({ availableSwitchDevices.push({
key: d.id, key: d.id,
@ -271,7 +273,7 @@ class NewDevice extends Component {
value: d.id, value: d.id,
}); });
} }
if (d.kind === "dimmableLight") { if (d.kind === 'dimmableLight') {
availableDimmerDevices.push({ availableDimmerDevices.push({
key: d.id, key: d.id,
text: d.name, text: d.name,
@ -296,7 +298,7 @@ class NewDevice extends Component {
<label>Device Name: </label> <label>Device Name: </label>
<Input <Input
fluid fluid
size={"large"} size="large"
onChange={this.setDeviceName} onChange={this.setDeviceName}
focus focus
placeholder="Device Name" placeholder="Device Name"
@ -305,7 +307,7 @@ class NewDevice extends Component {
</div> </div>
); );
const sensorForm = ( const sensorForm = (
<Form.Field style={{ marginTop: "1rem" }}> <Form.Field style={{ marginTop: '1rem' }}>
<label>Type of Sensor: </label> <label>Type of Sensor: </label>
<Dropdown <Dropdown
name="typeOfDevice" name="typeOfDevice"
@ -318,7 +320,7 @@ class NewDevice extends Component {
</Form.Field> </Form.Field>
); );
const switchOptions = ( const switchOptions = (
<Form.Field style={{ marginTop: "1rem" }}> <Form.Field style={{ marginTop: '1rem' }}>
<label>Select the lights or smart plugs You Want to Attach: </label> <label>Select the lights or smart plugs You Want to Attach: </label>
<Dropdown <Dropdown
name="typeOfDevice" name="typeOfDevice"
@ -331,7 +333,7 @@ class NewDevice extends Component {
</Form.Field> </Form.Field>
); );
const dimmerOptions = ( const dimmerOptions = (
<Form.Field style={{ marginTop: "1rem" }}> <Form.Field style={{ marginTop: '1rem' }}>
<label>Select the dimmable lights You Want to Attach: </label> <label>Select the dimmable lights You Want to Attach: </label>
<Dropdown <Dropdown
name="typeOfDevice" name="typeOfDevice"
@ -346,12 +348,12 @@ class NewDevice extends Component {
return ( return (
<Form> <Form>
{deviceName} {deviceName}
{this.state.typeOfDevice === "sensor" ? sensorForm : ""} {this.state.typeOfDevice === 'sensor' ? sensorForm : ''}
{this.state.typeOfDevice === "switch" ? switchOptions : ""} {this.state.typeOfDevice === 'switch' ? switchOptions : ''}
{this.state.typeOfDevice === "buttonDimmer" || {this.state.typeOfDevice === 'buttonDimmer'
this.state.typeOfDevice === "knobDimmer" || this.state.typeOfDevice === 'knobDimmer'
? dimmerOptions ? dimmerOptions
: ""} : ''}
</Form> </Form>
); );
}; };
@ -361,19 +363,19 @@ class NewDevice extends Component {
closeIcon closeIcon
open={this.state.openModal} open={this.state.openModal}
onClose={this.resetState} onClose={this.resetState}
trigger={ trigger={(
<StyledDiv <StyledDiv
onClick={this.handleOpen} onClick={this.handleOpen}
style={{ style={{
position: "relative", position: 'relative',
top: "calc(50% - 5rem)", top: 'calc(50% - 5rem)',
left: "calc(50% - 5rem)", left: 'calc(50% - 5rem)',
}} }}
> >
<Image src="/img/add.svg" style={{ filter: "invert()" }} /> <Image src="/img/add.svg" style={{ filter: 'invert()' }} />
</StyledDiv> </StyledDiv>
} )}
centered={true} centered
> >
<Modal.Header>Add a New Device</Modal.Header> <Modal.Header>Add a New Device</Modal.Header>
<Modal.Content>{steps[this.state.step - 1]}</Modal.Content> <Modal.Content>{steps[this.state.step - 1]}</Modal.Content>
@ -389,7 +391,7 @@ class NewDevice extends Component {
Back Back
</Button> </Button>
) : ( ) : (
"" ''
)} )}
{this.state.step < steps.length ? ( {this.state.step < steps.length ? (
<Button <Button
@ -402,7 +404,7 @@ class NewDevice extends Component {
<Icon name="right arrow" /> <Icon name="right arrow" />
</Button> </Button>
) : ( ) : (
"" ''
)} )}
{this.state.step === steps.length ? ( {this.state.step === steps.length ? (
<Button <Button
@ -415,7 +417,7 @@ class NewDevice extends Component {
Finish Finish
</Button> </Button>
) : ( ) : (
"" ''
)} )}
</Modal.Actions> </Modal.Actions>
</Modal> </Modal>

View file

@ -19,8 +19,10 @@
errorStyle, errorStyle,
*/ */
import React, { Component } from "react"; import React, { Component } from 'react';
import { CircularInput, CircularProgress } from "react-circular-input"; import { CircularInput, CircularProgress } from 'react-circular-input';
import { Image } from 'semantic-ui-react';
import { connect } from 'react-redux';
import { import {
container, container,
sensorText, sensorText,
@ -34,11 +36,9 @@ import {
lightSensorColors, lightSensorColors,
humiditySensorColors, humiditySensorColors,
iconSensorStyle, iconSensorStyle,
} from "./SensorStyle"; } from './SensorStyle';
import { Image } from "semantic-ui-react"; import { RemoteService } from '../../../remote';
import { RemoteService } from "../../../remote"; import mapStateToProps from '../../../deviceProps';
import { connect } from "react-redux";
import mapStateToProps from "../../../deviceProps";
class Sensor extends Component { class Sensor extends Component {
constructor(props) { constructor(props) {
@ -47,25 +47,25 @@ class Sensor extends Component {
value: 0, value: 0,
motion: false, motion: false,
}; };
this.units = ""; this.units = '';
this.stateCallback = (e) => { this.stateCallback = (e) => {
this.setState(Object.assign(this.state, e)); this.setState(Object.assign(this.state, e));
}; };
this.colors = temperatureSensorColors; this.colors = temperatureSensorColors;
this.icon = "temperatureIcon.svg"; this.icon = 'temperatureIcon.svg';
this.name = "Sensor"; this.name = 'Sensor';
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
if ( if (
this.props.stateOrDevice.kind === "sensor" && this.props.stateOrDevice.kind === 'sensor'
this.props.stateOrDevice.value !== prevProps.stateOrDevice.value && this.props.stateOrDevice.value !== prevProps.stateOrDevice.value
) { ) {
this.setState({ value: this.props.stateOrDevice.value }); this.setState({ value: this.props.stateOrDevice.value });
} else if ( } else if (
this.props.stateOrDevice.kind === "motionSensor" && this.props.stateOrDevice.kind === 'motionSensor'
this.props.stateOrDevice.detected !== prevProps.stateOrDevice.detected && this.props.stateOrDevice.detected !== prevProps.stateOrDevice.detected
) { ) {
this.setState({ this.setState({
motion: true, motion: true,
@ -75,28 +75,28 @@ class Sensor extends Component {
} }
componentDidMount() { componentDidMount() {
if (this.props.stateOrDevice.kind === "sensor") { if (this.props.stateOrDevice.kind === 'sensor') {
switch (this.props.stateOrDevice.sensor) { switch (this.props.stateOrDevice.sensor) {
case "TEMPERATURE": case 'TEMPERATURE':
this.units = "ºC"; this.units = 'ºC';
this.colors = temperatureSensorColors; this.colors = temperatureSensorColors;
this.icon = "temperatureIcon.svg"; this.icon = 'temperatureIcon.svg';
this.name = "Temperature Sensor"; this.name = 'Temperature Sensor';
break; break;
case "HUMIDITY": case 'HUMIDITY':
this.units = "%"; this.units = '%';
this.colors = humiditySensorColors; this.colors = humiditySensorColors;
this.icon = "humidityIcon.svg"; this.icon = 'humidityIcon.svg';
this.name = "Humidity Sensor"; this.name = 'Humidity Sensor';
break; break;
case "LIGHT": case 'LIGHT':
this.units = "lm"; this.units = 'lm';
this.colors = lightSensorColors; this.colors = lightSensorColors;
this.icon = "lightSensorIcon.svg"; this.icon = 'lightSensorIcon.svg';
this.name = "Light Sensor"; this.name = 'Light Sensor';
break; break;
default: default:
this.units = ""; this.units = '';
} }
this.setState({ this.setState({
value: this.props.stateOrDevice.value, value: this.props.stateOrDevice.value,
@ -129,18 +129,17 @@ class Sensor extends Component {
}; };
render() { render() {
const MotionSensor = (props) => { const MotionSensor = (props) => (
return (
<div <div
style={{ style={{
...motionSensorOuterCircle, ...motionSensorOuterCircle,
backgroundColor: this.state.detected ? "#505bda" : "#00bdaa", backgroundColor: this.state.detected ? '#505bda' : '#00bdaa',
}} }}
> >
<div <div
style={{ style={{
...motionSensorInnerCircle, ...motionSensorInnerCircle,
backgroundColor: this.state.detected ? "#fe346e" : "#00bdaa", backgroundColor: this.state.detected ? '#fe346e' : '#00bdaa',
}} }}
> >
<Image style={motionSensorIcon} src="/img/motionSensorIcon.svg" /> <Image style={motionSensorIcon} src="/img/motionSensorIcon.svg" />
@ -148,17 +147,16 @@ class Sensor extends Component {
</div> </div>
</div> </div>
); );
};
return ( return (
<div style={container}> <div style={container}>
{this.state.motion ? ( {this.state.motion ? (
<MotionSensor /> <MotionSensor />
) : ( ) : (
<React.Fragment> <>
<CircularInput <CircularInput
value={ value={
this.props.stateOrDevice.sensor === "LIGHT" this.props.stateOrDevice.sensor === 'LIGHT'
? this.state.value / 2000 ? this.state.value / 2000
: this.state.value / 100 : this.state.value / 100
} }
@ -167,7 +165,7 @@ class Sensor extends Component {
<CircularProgress <CircularProgress
strokeWidth="2rem" strokeWidth="2rem"
stroke={ stroke={
this.props.stateOrDevice.sensor === "TEMPERATURE" this.props.stateOrDevice.sensor === 'TEMPERATURE'
? this.temperatureColor(this.state.value) ? this.temperatureColor(this.state.value)
: this.colors.progress : this.colors.progress
} }
@ -185,7 +183,7 @@ class Sensor extends Component {
fontWeight="bold" fontWeight="bold"
fill={this.colors.text} fill={this.colors.text}
> >
{+(Math.round(this.state.value + "e+2") + "e-2")} {+(`${Math.round(`${this.state.value}e+2`)}e-2`)}
{this.units} {this.units}
</text> </text>
<text <text
@ -203,7 +201,7 @@ class Sensor extends Component {
</text> </text>
</CircularInput> </CircularInput>
<Image style={iconSensorStyle} src={`/img/${this.icon}`} /> <Image style={iconSensorStyle} src={`/img/${this.icon}`} />
</React.Fragment> </>
)} )}
</div> </div>
); );

View file

@ -1,108 +1,108 @@
export const style = { export const style = {
width: "10rem", width: '10rem',
height: "10rem", height: '10rem',
position: "absolute", position: 'absolute',
top: "0", top: '0',
left: "0", left: '0',
}; };
export const container = { export const container = {
width: "10rem", width: '10rem',
height: "10rem", height: '10rem',
borderRadius: "100%", borderRadius: '100%',
border: "none", border: 'none',
position: "relative", position: 'relative',
}; };
export const sensorText = { export const sensorText = {
fill: "#3e99ff", fill: '#3e99ff',
fontSize: "1.2rem", fontSize: '1.2rem',
fontFamily: "Lato", fontFamily: 'Lato',
overflow: "hidden", overflow: 'hidden',
whiteSpace: "nowrap", whiteSpace: 'nowrap',
textOverflow: "ellipsis", textOverflow: 'ellipsis',
userSelect: "none", userSelect: 'none',
}; };
export const valueStyle = { export const valueStyle = {
fill: "#3e99ff", fill: '#3e99ff',
fontSize: "2.4rem", fontSize: '2.4rem',
fontFamily: "Lato", fontFamily: 'Lato',
userSelect: "none", userSelect: 'none',
}; };
export const errorStyle = { export const errorStyle = {
fill: "#ff4050", fill: '#ff4050',
fontSize: "1.5rem", fontSize: '1.5rem',
fontFamily: "Lato", fontFamily: 'Lato',
textShadow: "1px 1px 0.5px rgba(0, 0, 0, .2)", textShadow: '1px 1px 0.5px rgba(0, 0, 0, .2)',
userSelect: "none", userSelect: 'none',
}; };
export const motionSensorInnerCircle = { export const motionSensorInnerCircle = {
position: "absolute", position: 'absolute',
top: "50%", top: '50%',
left: "50%", left: '50%',
transform: "translate(-50%, -50%)", transform: 'translate(-50%, -50%)',
width: "8rem", width: '8rem',
height: "8rem", height: '8rem',
borderRadius: "100%", borderRadius: '100%',
border: "none", border: 'none',
}; };
export const motionSensorOuterCircle = { export const motionSensorOuterCircle = {
textAlign: "center", textAlign: 'center',
cursor: "pointer", cursor: 'pointer',
position: "relative", position: 'relative',
width: "10rem", width: '10rem',
height: "10rem", height: '10rem',
borderRadius: "100%", borderRadius: '100%',
border: "none", border: 'none',
/*boxShadow: "3px 2px 10px 5px #ccc",*/ /* boxShadow: "3px 2px 10px 5px #ccc", */
}; };
export const nameMotionStyle = { export const nameMotionStyle = {
position: "absolute", position: 'absolute',
top: "50%", top: '50%',
left: "50%", left: '50%',
transform: "translateX(-50%)", transform: 'translateX(-50%)',
fontSize: "1.2rem", fontSize: '1.2rem',
overflow: "hidden", overflow: 'hidden',
whiteSpace: "nowrap", whiteSpace: 'nowrap',
textOverflow: "ellipsis", textOverflow: 'ellipsis',
}; };
export const motionSensorIcon = { export const motionSensorIcon = {
width: "2rem", width: '2rem',
height: "2rem", height: '2rem',
position: "absolute", position: 'absolute',
top: "15%", top: '15%',
left: "50%", left: '50%',
transform: "translateX(-50%)", transform: 'translateX(-50%)',
}; };
export const temperatureSensorColors = { export const temperatureSensorColors = {
circle: "#323232", circle: '#323232',
progress: "#ff1e56", progress: '#ff1e56',
text: "white", text: 'white',
}; };
export const lightSensorColors = { export const lightSensorColors = {
circle: "#000839", circle: '#000839',
progress: "#ffa41b", progress: '#ffa41b',
text: "white", text: 'white',
}; };
export const humiditySensorColors = { export const humiditySensorColors = {
circle: "#005082", circle: '#005082',
progress: "#00a8cc", progress: '#00a8cc',
text: "white", text: 'white',
}; };
export const iconSensorStyle = { export const iconSensorStyle = {
position: "absolute", position: 'absolute',
top: "20%", top: '20%',
left: "50%", left: '50%',
transform: "translateX(-50%)", transform: 'translateX(-50%)',
width: "2rem", width: '2rem',
height: "2rem", height: '2rem',
}; };

View file

@ -3,25 +3,25 @@
SmartHut interface or by a switch. SmartHut interface or by a switch.
The smart plug also stores the total energy consumed while the plug is active, in terms of kilowatt-hours 2(kWh) . The smart plug also stores the total energy consumed while the plug is active, in terms of kilowatt-hours 2(kWh) .
The user can reset this value. The user can reset this value.
**/ * */
import React, { Component } from "react"; import React, { Component } from 'react';
import { BottomPanel, StyledDiv } from "./styleComponents"; import { Image } from 'semantic-ui-react';
import { Image } from "semantic-ui-react"; import { connect } from 'react-redux';
import { BottomPanel, StyledDiv } from './styleComponents';
import { import {
energyConsumedStyle, energyConsumedStyle,
imageStyle, imageStyle,
kwhStyle, kwhStyle,
nameStyle, nameStyle,
} from "./SmartPlugStyle"; } from './SmartPlugStyle';
import { RemoteService } from "../../../remote"; import { RemoteService } from '../../../remote';
import { connect } from "react-redux"; import mapStateToProps from '../../../deviceProps';
import mapStateToProps from "../../../deviceProps";
class SmartPlug extends Component { class SmartPlug extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.iconOn = "/img/smart-plug.svg"; this.iconOn = '/img/smart-plug.svg';
this.iconOff = "/img/smart-plug-off.svg"; this.iconOff = '/img/smart-plug-off.svg';
} }
get turnedOn() { get turnedOn() {
@ -34,21 +34,19 @@ class SmartPlug extends Component {
onClickDevice = () => { onClickDevice = () => {
const on = !this.turnedOn; const on = !this.turnedOn;
if (this.props.tab === "Devices") { if (this.props.tab === 'Devices') {
this.props this.props
.saveDevice({ ...this.props.stateOrDevice, on }) .saveDevice({ ...this.props.stateOrDevice, on })
.catch((err) => console.error("smart plug update error", err)); .catch((err) => console.error('smart plug update error', err));
} else { } else {
this.props.updateState( this.props.updateState(
{ id: this.props.stateOrDevice.id, on: on }, { id: this.props.stateOrDevice.id, on },
this.props.stateOrDevice.kind this.props.stateOrDevice.kind,
); );
} }
}; };
getIcon = () => { getIcon = () => (this.turnedOn ? this.iconOn : this.iconOff);
return this.turnedOn ? this.iconOn : this.iconOff;
};
render() { render() {
return ( return (
@ -59,8 +57,8 @@ class SmartPlug extends Component {
<BottomPanel <BottomPanel
style={ style={
this.turnedOn this.turnedOn
? { backgroundColor: "#505bda" } ? { backgroundColor: '#505bda' }
: { backgroundColor: "#1a2849" } : { backgroundColor: '#1a2849' }
} }
> >
<span style={energyConsumedStyle}>{this.energyConsumed}</span> <span style={energyConsumedStyle}>{this.energyConsumed}</span>

View file

@ -1,42 +1,42 @@
export const energyConsumedStyle = { export const energyConsumedStyle = {
color: "white", color: 'white',
fontSize: "1.3rem", fontSize: '1.3rem',
position: "absolute", position: 'absolute',
top: "20%", top: '20%',
left: "50%", left: '50%',
transform: "translateX(-50%)", transform: 'translateX(-50%)',
userSelect: "none", userSelect: 'none',
}; };
export const kwhStyle = { export const kwhStyle = {
color: "white", color: 'white',
fontSize: "1rem", fontSize: '1rem',
position: "absolute", position: 'absolute',
top: "50%", top: '50%',
left: "50%", left: '50%',
transform: "translateX(-50%)", transform: 'translateX(-50%)',
userSelect: "none", userSelect: 'none',
}; };
export const imageStyle = { export const imageStyle = {
width: "2rem", width: '2rem',
height: "auto", height: 'auto',
position: "absolute", position: 'absolute',
top: "5%", top: '5%',
left: "50%", left: '50%',
transform: "translateX(-35%)", transform: 'translateX(-35%)',
filter: "drop-shadow( 1px 1px 0.5px rgba(0, 0, 0, .25))", filter: 'drop-shadow( 1px 1px 0.5px rgba(0, 0, 0, .25))',
userSelect: "none", userSelect: 'none',
}; };
export const nameStyle = { export const nameStyle = {
color: "black", color: 'black',
position: "absolute", position: 'absolute',
top: "30%", top: '30%',
left: "50%", left: '50%',
transform: "translateX(-50%)", transform: 'translateX(-50%)',
overflow: "hidden", overflow: 'hidden',
whiteSpace: "nowrap", whiteSpace: 'nowrap',
textOverflow: "ellipsis", textOverflow: 'ellipsis',
userSelect: "none", userSelect: 'none',
}; };

View file

@ -5,35 +5,33 @@
* The user can change the state of a switch through the SmartHut interface. * The user can change the state of a switch through the SmartHut interface.
*/ */
import React, { Component } from "react"; import React, { Component } from 'react';
import { BottomPanel, StyledDiv } from "./styleComponents"; import { Image } from 'semantic-ui-react';
import { Image } from "semantic-ui-react"; import { connect } from 'react-redux';
import { imageStyle, nameStyle, turnedOnStyle } from "./SwitchStyle"; import { BottomPanel, StyledDiv } from './styleComponents';
import { RemoteService } from "../../../remote"; import { imageStyle, nameStyle, turnedOnStyle } from './SwitchStyle';
import { connect } from "react-redux"; import { RemoteService } from '../../../remote';
import mapStateToProps from "../../../deviceProps"; import mapStateToProps from '../../../deviceProps';
class Switch extends Component { class Switch extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.iconOn = "/img/switchOn.svg"; this.iconOn = '/img/switchOn.svg';
this.iconOff = "/img/switchOff.svg"; this.iconOff = '/img/switchOff.svg';
} }
get turnedOn() { get turnedOn() {
return this.props.device.on; return this.props.device.on;
} }
getIcon = () => { getIcon = () => (this.turnedOn ? this.iconOn : this.iconOff);
return this.turnedOn ? this.iconOn : this.iconOff;
};
onClickDevice = () => { onClickDevice = () => {
const newOn = !this.turnedOn; const newOn = !this.turnedOn;
const type = newOn ? "ON" : "OFF"; const type = newOn ? 'ON' : 'OFF';
this.props this.props
.switchOperate(this.props.id, type) .switchOperate(this.props.id, type)
.catch((err) => console.error("switch operate failed", err)); .catch((err) => console.error('switch operate failed', err));
}; };
render() { render() {
@ -45,11 +43,11 @@ class Switch extends Component {
<BottomPanel <BottomPanel
style={ style={
this.turnedOn this.turnedOn
? { backgroundColor: "#505bda" } ? { backgroundColor: '#505bda' }
: { backgroundColor: "#1a2849" } : { backgroundColor: '#1a2849' }
} }
> >
<span style={turnedOnStyle}>{this.turnedOn ? "ON" : "OFF"}</span> <span style={turnedOnStyle}>{this.turnedOn ? 'ON' : 'OFF'}</span>
</BottomPanel> </BottomPanel>
</StyledDiv> </StyledDiv>
); );

View file

@ -1,31 +1,31 @@
export const imageStyle = { export const imageStyle = {
width: "2rem", width: '2rem',
height: "auto", height: 'auto',
position: "absolute", position: 'absolute',
top: "5%", top: '5%',
left: "50%", left: '50%',
transform: "translateX(-50%)", transform: 'translateX(-50%)',
userSelect: "none", userSelect: 'none',
}; };
export const nameStyle = { export const nameStyle = {
color: "black", color: 'black',
position: "absolute", position: 'absolute',
top: "30%", top: '30%',
left: "50%", left: '50%',
transform: "translateX(-50%)", transform: 'translateX(-50%)',
overflow: "hidden", overflow: 'hidden',
whiteSpace: "nowrap", whiteSpace: 'nowrap',
textOverflow: "ellipsis", textOverflow: 'ellipsis',
userSelect: "none", userSelect: 'none',
}; };
export const turnedOnStyle = { export const turnedOnStyle = {
color: "white", color: 'white',
fontSize: "1.3rem", fontSize: '1.3rem',
position: "absolute", position: 'absolute',
top: "20%", top: '20%',
left: "50%", left: '50%',
transform: "translateX(-50%)", transform: 'translateX(-50%)',
userSelect: "none", userSelect: 'none',
}; };

View file

@ -1,63 +1,63 @@
export const container = { export const container = {
margin: "-1em 0", margin: '-1em 0',
width: "15em", width: '15em',
height: "15.5em", height: '15.5em',
boxShadow: "5px 5px 5px 5px #DDD", boxShadow: '5px 5px 5px 5px #DDD',
borderRadius: "1em", borderRadius: '1em',
backgroundColor: "white", backgroundColor: 'white',
transform: "translate(-2.5em, 0)", transform: 'translate(-2.5em, 0)',
}; };
export const deviceName = { export const deviceName = {
paddingTop: ".5rem", paddingTop: '.5rem',
paddingLeft: "1rem", paddingLeft: '1rem',
fontFamily: "Lato", fontFamily: 'Lato',
fontSize: "1rem", fontSize: '1rem',
fontWeight: "bold", fontWeight: 'bold',
}; };
export const targetTemperature = { export const targetTemperature = {
fontFamily: "Lato", fontFamily: 'Lato',
marginTop: ".5rem", marginTop: '.5rem',
width: "100%", width: '100%',
textAlign: "center", textAlign: 'center',
fontSize: "1.3rem", fontSize: '1.3rem',
fontWeight: "bold", fontWeight: 'bold',
color: "#646464", color: '#646464',
}; };
export const slider = { export const slider = {
width: "25rem", width: '25rem',
fontFamily: "Lato", fontFamily: 'Lato',
position: "absolute", position: 'absolute',
marginTop: "35%", marginTop: '35%',
marginLeft: "50%", marginLeft: '50%',
transform: "translate(-50%,-50%)", transform: 'translate(-50%,-50%)',
}; };
export const stateTagContainer = { export const stateTagContainer = {
textAlign: "center", textAlign: 'center',
position: "absolute", position: 'absolute',
width: "10rem", width: '10rem',
height: "2rem", height: '2rem',
bottom: "-.25rem", bottom: '-.25rem',
left: "50%", left: '50%',
transform: "translate(-50%,-50%)", transform: 'translate(-50%,-50%)',
backgroundColor: "#2b2", backgroundColor: '#2b2',
borderRadius: "50px", borderRadius: '50px',
}; };
export const stateTag = { export const stateTag = {
fontFamily: "Lato", fontFamily: 'Lato',
fontSize: "1.2rem", fontSize: '1.2rem',
lineHeight: "2rem", lineHeight: '2rem',
color: "white", color: 'white',
textTransform: "uppercase", textTransform: 'uppercase',
}; };
export const toggle = { export const toggle = {
position: "absolute", position: 'absolute',
top: ".7rem", top: '.7rem',
right: "2.5rem", right: '2.5rem',
transform: "rotate(-360deg)", transform: 'rotate(-360deg)',
}; };

View file

@ -1,11 +1,11 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import { Checkbox, Icon } from "semantic-ui-react"; import { Checkbox, Icon } from 'semantic-ui-react';
import { RemoteService } from "../../../remote"; import { RemoteService } from '../../../remote';
import { connect } from "react-redux"; import { connect } from 'react-redux';
import "./Thermostat.css"; import './Thermostat.css';
import Slider from "react-rangeslider"; import Slider from 'react-rangeslider';
import "react-rangeslider/lib/index.css"; import 'react-rangeslider/lib/index.css';
import mapStateToProps from "../../../deviceProps"; import mapStateToProps from '../../../deviceProps';
import { import {
stateTag, stateTag,
@ -14,7 +14,7 @@ import {
targetTemperature, targetTemperature,
toggle, toggle,
stateTagContainer, stateTagContainer,
} from "./ThermostatStyle"; } from './ThermostatStyle';
class Thermostats extends Component { class Thermostats extends Component {
constructor(props) { constructor(props) {
@ -29,53 +29,53 @@ class Thermostats extends Component {
} }
setMode(mode) { setMode(mode) {
//i came to the conclusion that is not possible to set mode. // i came to the conclusion that is not possible to set mode.
// Good job Jacob (Claudio) // Good job Jacob (Claudio)
//this.mode = "HEATING"; // this.mode = "HEATING";
const turnOn = mode; const turnOn = mode;
if (this.props.tab === "Devices") { if (this.props.tab === 'Devices') {
this.props this.props
.saveDevice({ ...this.props.stateOrDevice, turnOn }) .saveDevice({ ...this.props.stateOrDevice, turnOn })
.catch((err) => console.error("thermostat update error", err)); .catch((err) => console.error('thermostat update error', err));
} else { } else {
this.props.updateState( this.props.updateState(
{ id: this.props.stateOrDevice.id, on: turnOn }, { id: this.props.stateOrDevice.id, on: turnOn },
this.props.stateOrDevice.kind this.props.stateOrDevice.kind,
); );
} }
} }
onClickDevice = () => { onClickDevice = () => {
const on = !this.turnedOn; const on = !this.turnedOn;
if (this.props.tab === "Devices") { if (this.props.tab === 'Devices') {
this.props this.props
.saveDevice({ ...this.props.stateOrDevice, on }) .saveDevice({ ...this.props.stateOrDevice, on })
.catch((err) => console.error("thermostat update error", err)); .catch((err) => console.error('thermostat update error', err));
} else { } else {
this.props.updateState( this.props.updateState(
{ id: this.props.stateOrDevice.id, on }, { id: this.props.stateOrDevice.id, on },
this.props.stateOrDevice.kind this.props.stateOrDevice.kind,
); );
} }
}; };
saveTargetTemperature(targetTemperature) { saveTargetTemperature(targetTemperature) {
const turn = this.props.stateOrDevice.mode !== "OFF"; const turn = this.props.stateOrDevice.mode !== 'OFF';
if (this.props.tab === "Devices") { if (this.props.tab === 'Devices') {
this.props this.props
.saveDevice({ .saveDevice({
...this.props.stateOrDevice, ...this.props.stateOrDevice,
targetTemperature, targetTemperature,
turnOn: turn, turnOn: turn,
}) })
.catch((err) => console.error("thermostat update error", err)); .catch((err) => console.error('thermostat update error', err));
} else { } else {
this.props.updateState( this.props.updateState(
{ {
id: this.props.stateOrDevice.id, id: this.props.stateOrDevice.id,
targetTemperature: targetTemperature, targetTemperature,
}, },
this.props.stateOrDevice.kind this.props.stateOrDevice.kind,
); );
} }
} }
@ -90,11 +90,11 @@ class Thermostats extends Component {
handleCheckbox = (val) => { handleCheckbox = (val) => {
const useExternalSensors = val; const useExternalSensors = val;
const turnOn = this.props.stateOrDevice.mode !== "OFF"; const turnOn = this.props.stateOrDevice.mode !== 'OFF';
if (this.props.tab === "Devices") { if (this.props.tab === 'Devices') {
this.props this.props
.saveDevice({ ...this.props.stateOrDevice, useExternalSensors, turnOn }) .saveDevice({ ...this.props.stateOrDevice, useExternalSensors, turnOn })
.catch((err) => console.error("thermostat update error", err)); .catch((err) => console.error('thermostat update error', err));
} }
}; };
@ -106,8 +106,8 @@ class Thermostats extends Component {
<Checkbox <Checkbox
disabled={this.props.disabled} disabled={this.props.disabled}
checked={ checked={
this.props.tab === "Devices" this.props.tab === 'Devices'
? this.props.device.mode !== "OFF" ? this.props.device.mode !== 'OFF'
: this.props.stateOrDevice.on : this.props.stateOrDevice.on
} }
toggle toggle
@ -117,13 +117,20 @@ class Thermostats extends Component {
</h3> </h3>
<hr /> <hr />
<div style={targetTemperature}> <div style={targetTemperature}>
<Icon name="thermometer half" />{" "} <Icon name="thermometer half" />
{this.props.device.measuredTemperature} ºC <br /> {' '}
<Icon name="target" />{" "} {this.props.device.measuredTemperature}
{this.props.device.targetTemperature.toFixed(1)} ºC {' '}
ºC
<br />
<Icon name="target" />
{' '}
{this.props.device.targetTemperature.toFixed(1)}
{' '}
ºC
</div> </div>
{this.props.tab === "Devices" ? ( {this.props.tab === 'Devices' ? (
<React.Fragment> <>
<Slider <Slider
disabled={this.props.disabled} disabled={this.props.disabled}
min={10} min={10}
@ -136,7 +143,7 @@ class Thermostats extends Component {
onChangeComplete={() => this.setTargetTemperature()} onChangeComplete={() => this.setTargetTemperature()}
/> />
<Checkbox <Checkbox
style={{ padding: "0 .7rem" }} style={{ padding: '0 .7rem' }}
label="Use external sensors" label="Use external sensors"
name="external" name="external"
toggle toggle
@ -144,16 +151,16 @@ class Thermostats extends Component {
disabled={!this.props.tempSensorsInRoom} disabled={!this.props.tempSensorsInRoom}
onChange={(e, val) => this.handleCheckbox(val.checked)} onChange={(e, val) => this.handleCheckbox(val.checked)}
/> />
</React.Fragment> </>
) : null} ) : null}
<div style={stateTagContainer}> <div style={stateTagContainer}>
<span style={stateTag}> <span style={stateTag}>
{this.props.tab !== "Scenes" {this.props.tab !== 'Scenes'
? this.props.device.mode ? this.props.device.mode
: this.props.stateOrDevice.on : this.props.stateOrDevice.on
? "WILL TURN ON" ? 'WILL TURN ON'
: "WILL TURN OFF"} : 'WILL TURN OFF'}
</span> </span>
</div> </div>
</div> </div>
@ -164,13 +171,13 @@ class Thermostats extends Component {
const mapStateToProps2 = (state, ownProps) => ({ const mapStateToProps2 = (state, ownProps) => ({
...mapStateToProps(state, ownProps), ...mapStateToProps(state, ownProps),
get tempSensorsInRoom() { get tempSensorsInRoom() {
if (state.active.activeTab !== "Devices") return false; if (state.active.activeTab !== 'Devices') return false;
const room = state.rooms[state.devices[ownProps.id].roomId]; const room = state.rooms[state.devices[ownProps.id].roomId];
if (!room) return false; if (!room) return false;
const deviceIds = room.devices; const deviceIds = room.devices;
const devices = [...deviceIds].map((id) => state.devices[id]); const devices = [...deviceIds].map((id) => state.devices[id]);
const sensors = devices.filter( const sensors = devices.filter(
(d) => d.kind === "sensor" && d.sensor === "TEMPERATURE" (d) => d.kind === 'sensor' && d.sensor === 'TEMPERATURE',
); );
return sensors.length > 0; return sensors.length > 0;
}, },
@ -178,6 +185,6 @@ const mapStateToProps2 = (state, ownProps) => ({
const ThermostatContainer = connect( const ThermostatContainer = connect(
mapStateToProps2, mapStateToProps2,
RemoteService RemoteService,
)(Thermostats); )(Thermostats);
export default ThermostatContainer; export default ThermostatContainer;

View file

@ -1,24 +1,24 @@
export const LightDevice = { export const LightDevice = {
img: "/img/lightOff.svg", img: '/img/lightOff.svg',
imgClick: "/img/lightOn.svg", imgClick: '/img/lightOn.svg',
}; };
export const SmartPlugDevice = { export const SmartPlugDevice = {
img: "/img/smart-plug.svg", img: '/img/smart-plug.svg',
imgClick: "/img/smart-plug-off.svg", imgClick: '/img/smart-plug-off.svg',
}; };
export const TemperatureSensor = { export const TemperatureSensor = {
type: "temperature_sensor", type: 'temperature_sensor',
img: "", img: '',
imgClick: "", imgClick: '',
units: "ºC", units: 'ºC',
}; };
export const deviceList = [ export const deviceList = [
"Light", 'Light',
"Dimmer", 'Dimmer',
"Switcher", 'Switcher',
"Smart Plug", 'Smart Plug',
"Sensor", 'Sensor',
]; ];

View file

@ -1,13 +1,13 @@
// vim: set ts=2 sw=2 et tw=80: // vim: set ts=2 sw=2 et tw=80:
import React, { Component } from "react"; import React, { Component } from 'react';
import { StyledDivCamera } from "./styleComponents"; import { Grid, Checkbox } from 'semantic-ui-react';
import { Grid, Checkbox } from "semantic-ui-react"; import { connect } from 'react-redux';
import { RemoteService } from "../../../remote"; import { StyledDivCamera } from './styleComponents';
import { endpointURL } from "../../../endpoint"; import { RemoteService } from '../../../remote';
import { connect } from "react-redux"; import { endpointURL } from '../../../endpoint';
import VideocamModal from "./VideocamModal"; import VideocamModal from './VideocamModal';
import mapStateToProps from "../../../deviceProps"; import mapStateToProps from '../../../deviceProps';
class Videocam extends Component { class Videocam extends Component {
constructor(props) { constructor(props) {
@ -18,30 +18,24 @@ class Videocam extends Component {
} }
openModal = () => { openModal = () => {
this.setState((state) => { this.setState((state) => ({ selectedVideo: true }));
return { selectedVideo: true };
});
}; };
closeModal = () => { closeModal = () => {
this.setState((state) => { this.setState((state) => ({ selectedVideo: undefined }));
return { selectedVideo: undefined };
});
}; };
setOnOff(onOff) { setOnOff(onOff) {
const turn = onOff; const turn = onOff;
if (this.props.tab === "Devices" || this.props.tab === "Hosts") { if (this.props.tab === 'Devices' || this.props.tab === 'Hosts') {
this.props this.props
.saveDevice({ ...this.props.device, on: turn }) .saveDevice({ ...this.props.device, on: turn })
.then((res) => .then((res) => (turn ? this.refs.vidRef.play() : this.refs.vidRef.pause()))
turn ? this.refs.vidRef.play() : this.refs.vidRef.pause() .catch((err) => console.error('videocamera update error', err));
)
.catch((err) => console.error("videocamera update error", err));
} else { } else {
this.props.updateState( this.props.updateState(
{ id: this.props.stateOrDevice.id, on: turn }, { id: this.props.stateOrDevice.id, on: turn },
this.props.stateOrDevice.kind this.props.stateOrDevice.kind,
); );
} }
} }

View file

@ -1,5 +1,21 @@
import React from "react"; import React from 'react';
import Modal from "react-modal"; import Modal from 'react-modal';
import { Button } from 'semantic-ui-react';
const modal = {
opacity: 0,
alignItems: 'center',
display: 'flex',
justifyContent: 'center',
transition: 'opacity 200ms ease-in-out',
background: 'grey',
color: 'white',
maxWidth: '2rem',
outline: 'none',
padding: '2rem',
textAlign: 'center',
maxHeight: '50vh',
};
const VideocamModal = (props) => ( const VideocamModal = (props) => (
<Modal <Modal
@ -13,7 +29,7 @@ const VideocamModal = (props) => (
left: 0, left: 0,
right: 0, right: 0,
bottom: 0, bottom: 0,
backgroundColor: 'rgba(0,0,0,0.5)' backgroundColor: 'rgba(0,0,0,0.5)',
}, },
content: { content: {
position: 'absolute', position: 'absolute',
@ -28,8 +44,8 @@ const VideocamModal = (props) => (
borderRadius: '4px', borderRadius: '4px',
outline: 'none', outline: 'none',
padding: '20px', padding: '20px',
backgroundColor: 'black' backgroundColor: 'black',
} },
}} }}
> >
{props.selectedVideo && ( {props.selectedVideo && (

View file

@ -1,105 +1,105 @@
import styled from "styled-components"; import styled from 'styled-components';
import { useCircularInputContext } from "react-circular-input"; import { useCircularInputContext } from 'react-circular-input';
import { ValueStyle } from "./DimmerStyle"; import React from 'react';
import React from "react"; import { ValueStyle } from './DimmerStyle';
export const editButtonStyle = { export const editButtonStyle = {
position: "absolute", position: 'absolute',
top: "0", top: '0',
right: "0", right: '0',
backgroundColor: "#505bda", backgroundColor: '#505bda',
borderRadius: "0 0 0 20px", borderRadius: '0 0 0 20px',
border: "none", border: 'none',
padding: ".4rem 1.2rem", padding: '.4rem 1.2rem',
outline: "none", outline: 'none',
color: "white", color: 'white',
fontFamily: "Lato", fontFamily: 'Lato',
textTransform: "uppercase", textTransform: 'uppercase',
}; };
export const panelStyle = { export const panelStyle = {
backgroundColor: "#fafafa", backgroundColor: '#fafafa',
height: "85vh", height: '85vh',
padding: "0rem 3rem", padding: '0rem 3rem',
color: "#000000", color: '#000000',
overflow: "auto", overflow: 'auto',
maxHeight: "75vh", maxHeight: '75vh',
}; };
export const mobilePanelStyle = { export const mobilePanelStyle = {
backgroundColor: "#fafafa", backgroundColor: '#fafafa',
minHeight: "100vh", minHeight: '100vh',
padding: "0rem 3rem", padding: '0rem 3rem',
color: "#000000", color: '#000000',
}; };
export const editModeStyle = { export const editModeStyle = {
position: "absolute", position: 'absolute',
top: "15%", top: '15%',
right: "0", right: '0',
width: "1.5rem", width: '1.5rem',
height: "1.5rem", height: '1.5rem',
backgroundColor: "black", backgroundColor: 'black',
borderRadius: "100%", borderRadius: '100%',
zIndex: "1000", zIndex: '1000',
cursor: "pointer", cursor: 'pointer',
}; };
export const editModeStyleLeft = { export const editModeStyleLeft = {
position: "absolute", position: 'absolute',
top: "15%", top: '15%',
left: "0", left: '0',
width: "1.5rem", width: '1.5rem',
height: "1.5rem", height: '1.5rem',
backgroundColor: "white", backgroundColor: 'white',
borderRadius: "100%", borderRadius: '100%',
zIndex: "1000", zIndex: '1000',
cursor: "pointer", cursor: 'pointer',
}; };
export const editModeIconStyle = { export const editModeIconStyle = {
position: "absolute", position: 'absolute',
top: "50%", top: '50%',
left: "50%", left: '50%',
transform: "translate(-50%, -50%)", transform: 'translate(-50%, -50%)',
width: "0.75rem", width: '0.75rem',
height: "0.75rem", height: '0.75rem',
borderRadius: "20%", borderRadius: '20%',
zIndex: "101", zIndex: '101',
}; };
export const iconStyle = { export const iconStyle = {
width: "3.5rem", width: '3.5rem',
height: "auto", height: 'auto',
position: "absolute", position: 'absolute',
top: "10%", top: '10%',
left: "50%", left: '50%',
transform: "translateX(-50%)", transform: 'translateX(-50%)',
userSelect: "none", userSelect: 'none',
}; };
export const nameStyle = { export const nameStyle = {
position: "absolute", position: 'absolute',
top: "50%", top: '50%',
left: "50%", left: '50%',
transform: "translateX(-50%)", transform: 'translateX(-50%)',
}; };
export const formStyle = { export const formStyle = {
position: "absolute", position: 'absolute',
zIndex: "1000", zIndex: '1000',
width: "80rem", width: '80rem',
height: "10rem", height: '10rem',
padding: "1rem", padding: '1rem',
margin: "1rem", margin: '1rem',
borderRadius: "10%", borderRadius: '10%',
boxShadow: "1px 1px 5px 2px #5d5d5d", boxShadow: '1px 1px 5px 2px #5d5d5d',
backgroundColor: "#3e99ff", backgroundColor: '#3e99ff',
}; };
export const addDeviceFormStyle = { export const addDeviceFormStyle = {
maxWidth: "400px", maxWidth: '400px',
background: "#3e99ff", background: '#3e99ff',
paddingRight: "5rem", paddingRight: '5rem',
}; };
export const StyledDiv = styled.div` export const StyledDiv = styled.div`

View file

@ -1,24 +1,24 @@
function getStateOrDevice(state, ownProps) { function getStateOrDevice(state, ownProps) {
switch (state.active.activeTab) { switch (state.active.activeTab) {
case "Devices": case 'Devices':
return state.devices[ownProps.id]; return state.devices[ownProps.id];
case "Scenes": case 'Scenes':
return state.sceneStates[ownProps.id]; return state.sceneStates[ownProps.id];
case "Hosts": case 'Hosts':
return state.hostDevices[ownProps.hostId][ownProps.id]; return state.hostDevices[ownProps.hostId][ownProps.id];
default: default:
throw new Error( throw new Error(
`stateOrDevice has no value in tab "${state.active.activeTab}"` `stateOrDevice has no value in tab "${state.active.activeTab}"`,
); );
} }
} }
function getDevice(state, ownProps) { function getDevice(state, ownProps) {
switch (state.active.activeTab) { switch (state.active.activeTab) {
case "Scenes": case 'Scenes':
return state.devices[getStateOrDevice(state, ownProps).deviceId]; return state.devices[getStateOrDevice(state, ownProps).deviceId];
case "Devices": case 'Devices':
case "Hosts": case 'Hosts':
return getStateOrDevice(state, ownProps); return getStateOrDevice(state, ownProps);
default: default:
throw new Error(`device has no value in tab "${state.active.activeTab}"`); throw new Error(`device has no value in tab "${state.active.activeTab}"`);
@ -27,18 +27,18 @@ function getDevice(state, ownProps) {
function getRoomName(state, ownProps) { function getRoomName(state, ownProps) {
switch (state.active.activeTab) { switch (state.active.activeTab) {
case "Scenes": case 'Scenes':
case "Devices": case 'Devices':
return (state.rooms[getDevice(state, ownProps).roomId] || {}).name; return (state.rooms[getDevice(state, ownProps).roomId] || {}).name;
case "Hosts": case 'Hosts':
const hostRooms = state.hostRooms[ownProps.hostId]; const hostRooms = state.hostRooms[ownProps.hostId];
if (!hostRooms) return ""; if (!hostRooms) return '';
const room = hostRooms[getDevice(state, ownProps).roomId]; const room = hostRooms[getDevice(state, ownProps).roomId];
if (!room) return ""; if (!room) return '';
return room.name; return room.name;
default: default:
throw new Error( throw new Error(
`room name has no value in tab "${state.active.activeTab}"` `room name has no value in tab "${state.active.activeTab}"`,
); );
} }
} }
@ -60,9 +60,9 @@ export default function mapStateToProps(state, ownProps) {
}, },
get disabled() { get disabled() {
return ( return (
ownProps.tab === "Hosts" && ownProps.tab === 'Hosts'
["dimmableLight", "light"].indexOf(getDevice(state, ownProps).kind) === && ['dimmableLight', 'light'].indexOf(getDevice(state, ownProps).kind)
-1 === -1
); );
}, },
}; };

View file

@ -3,17 +3,17 @@
* @returns {String} endpoint URL * @returns {String} endpoint URL
*/ */
export function endpointURL() { export function endpointURL() {
return window.BACKEND_URL !== "__BACKEND_URL__" return window.BACKEND_URL !== '__BACKEND_URL__'
? window.BACKEND_URL ? window.BACKEND_URL
: "http://localhost:8080"; : 'http://localhost:8080';
} }
export function socketURL(token) { export function socketURL(token) {
const httpURL = new URL(endpointURL()); const httpURL = new URL(endpointURL());
const isSecure = httpURL.protocol === "https:"; const isSecure = httpURL.protocol === 'https:';
const protocol = isSecure ? "wss:" : "ws:"; const protocol = isSecure ? 'wss:' : 'ws:';
const port = httpURL.port || (isSecure ? 443 : 80); const port = httpURL.port || (isSecure ? 443 : 80);
const url = `${protocol}//${httpURL.hostname}:${port}/sensor-socket?token=${token}`; const url = `${protocol}//${httpURL.hostname}:${port}/sensor-socket?token=${token}`;
console.log("socket url: ", url); console.log('socket url: ', url);
return url; return url;
} }

View file

@ -1,9 +1,9 @@
import React from "react"; import React from 'react';
import ReactDOM from "react-dom"; import ReactDOM from 'react-dom';
import App from "./App"; import { Provider } from 'react-redux';
import * as serviceWorker from "./serviceWorker"; import App from './App';
import { Provider } from "react-redux"; import * as serviceWorker from './serviceWorker';
import smartHutStore from "./store"; import smartHutStore from './store';
const index = ( const index = (
<Provider store={smartHutStore}> <Provider store={smartHutStore}>
@ -11,5 +11,5 @@ const index = (
</Provider> </Provider>
); );
ReactDOM.render(index, document.getElementById("root")); ReactDOM.render(index, document.getElementById('root'));
serviceWorker.unregister(); serviceWorker.unregister();

File diff suppressed because it is too large Load diff

View file

@ -11,17 +11,17 @@
// opt-in, read https://bit.ly/CRA-PWA // opt-in, read https://bit.ly/CRA-PWA
const isLocalhost = Boolean( const isLocalhost = Boolean(
window.location.hostname === "localhost" || window.location.hostname === 'localhost'
// [::1] is the IPv6 localhost address. // [::1] is the IPv6 localhost address.
window.location.hostname === "[::1]" || || window.location.hostname === '[::1]'
// 127.0.0.0/8 are considered localhost for IPv4. // 127.0.0.0/8 are considered localhost for IPv4.
window.location.hostname.match( || window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/,
) ),
); );
export function register(config) { export function register(config) {
if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) { if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW. // The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
if (publicUrl.origin !== window.location.origin) { if (publicUrl.origin !== window.location.origin) {
@ -31,7 +31,7 @@ export function register(config) {
return; return;
} }
window.addEventListener("load", () => { window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) { if (isLocalhost) {
@ -42,8 +42,8 @@ export function register(config) {
// service worker/PWA documentation. // service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => { navigator.serviceWorker.ready.then(() => {
console.log( console.log(
"This web app is being served cache-first by a service " + 'This web app is being served cache-first by a service '
"worker. To learn more, visit https://bit.ly/CRA-PWA" + 'worker. To learn more, visit https://bit.ly/CRA-PWA',
); );
}); });
} else { } else {
@ -64,14 +64,14 @@ function registerValidSW(swUrl, config) {
return; return;
} }
installingWorker.onstatechange = () => { installingWorker.onstatechange = () => {
if (installingWorker.state === "installed") { if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) { if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched, // At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older // but the previous service worker will still serve the older
// content until all client tabs are closed. // content until all client tabs are closed.
console.log( console.log(
"New content is available and will be used when all " + 'New content is available and will be used when all '
"tabs for this page are closed. See https://bit.ly/CRA-PWA." + 'tabs for this page are closed. See https://bit.ly/CRA-PWA.',
); );
// Execute callback // Execute callback
@ -82,7 +82,7 @@ function registerValidSW(swUrl, config) {
// At this point, everything has been precached. // At this point, everything has been precached.
// It's the perfect time to display a // It's the perfect time to display a
// "Content is cached for offline use." message. // "Content is cached for offline use." message.
console.log("Content is cached for offline use."); console.log('Content is cached for offline use.');
// Execute callback // Execute callback
if (config && config.onSuccess) { if (config && config.onSuccess) {
@ -94,21 +94,21 @@ function registerValidSW(swUrl, config) {
}; };
}) })
.catch((error) => { .catch((error) => {
console.error("Error during service worker registration:", error); console.error('Error during service worker registration:', error);
}); });
} }
function checkValidServiceWorker(swUrl, config) { function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page. // Check if the service worker can be found. If it can't reload the page.
fetch(swUrl, { fetch(swUrl, {
headers: { "Service-Worker": "script" }, headers: { 'Service-Worker': 'script' },
}) })
.then((response) => { .then((response) => {
// Ensure service worker exists, and that we really are getting a JS file. // Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get("content-type"); const contentType = response.headers.get('content-type');
if ( if (
response.status === 404 || response.status === 404
(contentType != null && contentType.indexOf("javascript") === -1) || (contentType != null && contentType.indexOf('javascript') === -1)
) { ) {
// No service worker found. Probably a different app. Reload the page. // No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then((registration) => { navigator.serviceWorker.ready.then((registration) => {
@ -123,13 +123,13 @@ function checkValidServiceWorker(swUrl, config) {
}) })
.catch(() => { .catch(() => {
console.log( console.log(
"No internet connection found. App is running in offline mode." 'No internet connection found. App is running in offline mode.',
); );
}); });
} }
export function unregister() { export function unregister() {
if ("serviceWorker" in navigator) { if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready navigator.serviceWorker.ready
.then((registration) => { .then((registration) => {
registration.unregister(); registration.unregister();

View file

@ -2,4 +2,4 @@
// allows you to do things like: // allows you to do things like:
// expect(element).toHaveTextContent(/react/i) // expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom // learn more: https://github.com/testing-library/jest-dom
import "@testing-library/jest-dom/extend-expect"; import '@testing-library/jest-dom/extend-expect';

View file

@ -1,11 +1,12 @@
import { createStore, applyMiddleware, compose } from "redux"; import { createStore, applyMiddleware, compose } from 'redux';
import thunk from "redux-thunk"; import thunk from 'redux-thunk';
import update from "immutability-helper"; import update from 'immutability-helper';
import reduxWebSocket, { connect } from "@giantmachines/redux-websocket"; import reduxWebSocket, { connect } from '@giantmachines/redux-websocket';
import { socketURL } from "./endpoint"; import { socketURL } from './endpoint';
function reducer(previousState, action) { function reducer(previousState, action) {
let newState, change; let newState; let
change;
const createOrUpdateRoom = (room) => { const createOrUpdateRoom = (room) => {
if (!newState.rooms[room.id]) { if (!newState.rooms[room.id]) {
@ -89,21 +90,21 @@ function reducer(previousState, action) {
}; };
switch (action.type) { switch (action.type) {
case "LOGIN_UPDATE": case 'LOGIN_UPDATE':
newState = update(previousState, { login: { $set: action.login } }); newState = update(previousState, { login: { $set: action.login } });
break; break;
case "USER_INFO_UPDATE": case 'USER_INFO_UPDATE':
newState = update(previousState, { newState = update(previousState, {
userInfo: { $set: action.userInfo }, userInfo: { $set: action.userInfo },
}); });
break; break;
case "ROOMS_UPDATE": case 'ROOMS_UPDATE':
newState = previousState; newState = previousState;
for (const room of action.rooms) { for (const room of action.rooms) {
createOrUpdateRoom(room); createOrUpdateRoom(room);
} }
break; break;
case "HOST_ROOMS_UPDATE": case 'HOST_ROOMS_UPDATE':
change = { change = {
hostRooms: { hostRooms: {
[action.hostId]: { $set: {} }, [action.hostId]: { $set: {} },
@ -117,13 +118,13 @@ function reducer(previousState, action) {
newState = update(previousState, change); newState = update(previousState, change);
break; break;
case "SCENES_UPDATE": case 'SCENES_UPDATE':
newState = previousState; newState = previousState;
for (const scene of action.scenes) { for (const scene of action.scenes) {
createOrUpdateScene(scene); createOrUpdateScene(scene);
} }
break; break;
case "HOST_SCENES_UPDATE": case 'HOST_SCENES_UPDATE':
change = { change = {
hostScenes: { hostScenes: {
[action.hostId]: { $set: action.scenes }, // stored as array [action.hostId]: { $set: action.scenes }, // stored as array
@ -132,8 +133,8 @@ function reducer(previousState, action) {
newState = update(previousState, change); newState = update(previousState, change);
break; break;
case "STATES_UPDATE": case 'STATES_UPDATE':
//console.log(action.sceneStates); // console.log(action.sceneStates);
change = null; change = null;
// if scene is given, delete all sceneStates in that scene // if scene is given, delete all sceneStates in that scene
@ -169,11 +170,9 @@ function reducer(previousState, action) {
} }
if (sceneState.sceneId in newState.scenes) { if (sceneState.sceneId in newState.scenes) {
change.scenes[sceneState.sceneId] = change.scenes[sceneState.sceneId] = change.scenes[sceneState.sceneId] || {};
change.scenes[sceneState.sceneId] || {}; change.scenes[sceneState.sceneId].sceneStates = change.scenes[sceneState.sceneId].sceneStates || {};
change.scenes[sceneState.sceneId].sceneStates = const { sceneStates } = change.scenes[sceneState.sceneId];
change.scenes[sceneState.sceneId].sceneStates || {};
const sceneStates = change.scenes[sceneState.sceneId].sceneStates;
sceneStates.$add = sceneStates.$add || []; sceneStates.$add = sceneStates.$add || [];
sceneStates.$add.push(sceneState.id); sceneStates.$add.push(sceneState.id);
} else { } else {
@ -186,7 +185,7 @@ function reducer(previousState, action) {
}; };
} else { } else {
change.pendingJoins.scenes[sceneState.sceneId].$set.add( change.pendingJoins.scenes[sceneState.sceneId].$set.add(
sceneState.id sceneState.id,
); );
} }
} }
@ -195,7 +194,7 @@ function reducer(previousState, action) {
newState = update(newState, change); newState = update(newState, change);
break; break;
case "DEVICES_UPDATE": case 'DEVICES_UPDATE':
change = null; change = null;
// if room is given, delete all devices in that room // if room is given, delete all devices in that room
@ -225,7 +224,7 @@ function reducer(previousState, action) {
for (const device of action.devices) { for (const device of action.devices) {
if (!previousState.devices[device.id]) continue; if (!previousState.devices[device.id]) continue;
change.devices.$unset.push(device.id); change.devices.$unset.push(device.id);
const roomId = previousState.devices[device.id].roomId; const { roomId } = previousState.devices[device.id];
if (roomId in previousState.rooms) { if (roomId in previousState.rooms) {
change.rooms[roomId] = change.rooms[roomId] || { change.rooms[roomId] = change.rooms[roomId] || {
@ -265,9 +264,8 @@ function reducer(previousState, action) {
if (device.roomId in newState.rooms) { if (device.roomId in newState.rooms) {
change.rooms[device.roomId] = change.rooms[device.roomId] || {}; change.rooms[device.roomId] = change.rooms[device.roomId] || {};
change.rooms[device.roomId].devices = change.rooms[device.roomId].devices = change.rooms[device.roomId].devices || {};
change.rooms[device.roomId].devices || {}; const { devices } = change.rooms[device.roomId];
const devices = change.rooms[device.roomId].devices;
devices.$add = devices.$add || []; devices.$add = devices.$add || [];
devices.$add.push(device.id); devices.$add.push(device.id);
} else { } else {
@ -286,14 +284,13 @@ function reducer(previousState, action) {
newState = update(newState, change); newState = update(newState, change);
break; break;
case "HOST_DEVICES_UPDATE": case 'HOST_DEVICES_UPDATE':
newState = action.partial newState = action.partial
? previousState ? previousState
: update(previousState, { : update(previousState, {
hostDevices: { [action.hostId]: { $set: {} } }, hostDevices: { [action.hostId]: { $set: {} } },
}); });
newState.hostDevices[action.hostId] = newState.hostDevices[action.hostId] = newState.hostDevices[action.hostId] || {};
newState.hostDevices[action.hostId] || {};
change = { change = {
hostDevices: { hostDevices: {
[action.hostId]: {}, [action.hostId]: {},
@ -307,7 +304,7 @@ function reducer(previousState, action) {
newState = update(newState, change); newState = update(newState, change);
break; break;
case "AUTOMATION_UPDATE": case 'AUTOMATION_UPDATE':
const automations = {}; const automations = {};
for (const automation of action.automations) { for (const automation of action.automations) {
automations[automation.id] = automation; automations[automation.id] = automation;
@ -318,15 +315,15 @@ function reducer(previousState, action) {
}; };
newState = update(previousState, change); newState = update(previousState, change);
break; break;
case "ROOM_SAVE": case 'ROOM_SAVE':
newState = previousState; newState = previousState;
createOrUpdateRoom(action.room); createOrUpdateRoom(action.room);
break; break;
case "SCENE_SAVE": case 'SCENE_SAVE':
newState = previousState; newState = previousState;
createOrUpdateScene(action.scene); createOrUpdateScene(action.scene);
break; break;
case "DEVICE_SAVE": case 'DEVICE_SAVE':
change = { change = {
devices: { [action.device.id]: { $set: action.device } }, devices: { [action.device.id]: { $set: action.device } },
}; };
@ -350,7 +347,7 @@ function reducer(previousState, action) {
} }
newState = update(previousState, change); newState = update(previousState, change);
break; break;
case "HOST_DEVICE_SAVE": case 'HOST_DEVICE_SAVE':
change = { change = {
hostDevices: { hostDevices: {
[action.hostId]: { [action.hostId]: {
@ -362,7 +359,7 @@ function reducer(previousState, action) {
}; };
newState = update(previousState, change); newState = update(previousState, change);
break; break;
case "HOST_DEVICES_DELETE": case 'HOST_DEVICES_DELETE':
change = { change = {
hostDevices: { hostDevices: {
[action.hostId]: { [action.hostId]: {
@ -373,7 +370,7 @@ function reducer(previousState, action) {
newState = update(previousState, change); newState = update(previousState, change);
break; break;
case "AUTOMATION_SAVE": case 'AUTOMATION_SAVE':
change = { change = {
automations: { automations: {
[action.automation.id]: { $set: action.automation }, [action.automation.id]: { $set: action.automation },
@ -384,7 +381,7 @@ function reducer(previousState, action) {
break; break;
case "STATE_SAVE": case 'STATE_SAVE':
change = { change = {
sceneStates: { sceneStates: {
[action.sceneState.id]: { $set: action.sceneState }, [action.sceneState.id]: { $set: action.sceneState },
@ -410,7 +407,7 @@ function reducer(previousState, action) {
} }
newState = update(previousState, change); newState = update(previousState, change);
break; break;
case "ROOM_DELETE": case 'ROOM_DELETE':
if (!(action.roomId in previousState.rooms)) { if (!(action.roomId in previousState.rooms)) {
console.warn(`Room to delete ${action.roomId} does not exist`); console.warn(`Room to delete ${action.roomId} does not exist`);
break; break;
@ -434,14 +431,14 @@ function reducer(previousState, action) {
newState = update(previousState, change); newState = update(previousState, change);
break; break;
case "AUTOMATION_DELETE": case 'AUTOMATION_DELETE':
change = { change = {
automations: { $unset: [action.id] }, automations: { $unset: [action.id] },
}; };
newState = update(previousState, change); newState = update(previousState, change);
break; break;
case "SCENE_DELETE": case 'SCENE_DELETE':
if (!(action.sceneId in previousState.scenes)) { if (!(action.sceneId in previousState.scenes)) {
console.warn(`Scene to delete ${action.sceneId} does not exist`); console.warn(`Scene to delete ${action.sceneId} does not exist`);
break; break;
@ -464,7 +461,7 @@ function reducer(previousState, action) {
newState = update(previousState, change); newState = update(previousState, change);
break; break;
case "STATE_DELETE": case 'STATE_DELETE':
if (!(action.stateId in previousState.sceneStates)) { if (!(action.stateId in previousState.sceneStates)) {
console.warn(`State to delete ${action.stateId} does not exist`); console.warn(`State to delete ${action.stateId} does not exist`);
break; break;
@ -487,7 +484,7 @@ function reducer(previousState, action) {
newState = update(previousState, change); newState = update(previousState, change);
break; break;
case "DEVICE_DELETE": case 'DEVICE_DELETE':
if (!(action.deviceId in previousState.devices)) { if (!(action.deviceId in previousState.devices)) {
console.warn(`Device to delete ${action.deviceId} does not exist`); console.warn(`Device to delete ${action.deviceId} does not exist`);
break; break;
@ -507,10 +504,10 @@ function reducer(previousState, action) {
newState = update(previousState, change); newState = update(previousState, change);
break; break;
case "LOGOUT": case 'LOGOUT':
newState = update(initState, {}); newState = update(initState, {});
break; break;
case "SET_ACTIVE": case 'SET_ACTIVE':
newState = update(previousState, { newState = update(previousState, {
active: { active: {
[action.key]: { [action.key]: {
@ -519,17 +516,16 @@ function reducer(previousState, action) {
}, },
}); });
break; break;
case "REDUX_WEBSOCKET::MESSAGE": case 'REDUX_WEBSOCKET::MESSAGE':
const allDevices = JSON.parse(action.payload.message); const allDevices = JSON.parse(action.payload.message);
const devices = allDevices.filter( const devices = allDevices.filter(
(d) => (d) => (d.fromHostId === null || d.fromHostId === undefined) && !d.deleted,
(d.fromHostId === null || d.fromHostId === undefined) && !d.deleted
); );
const hostDevicesMapByHostId = allDevices const hostDevicesMapByHostId = allDevices
.filter((d) => d.fromHostId) .filter((d) => d.fromHostId)
.reduce((a, e) => { .reduce((a, e) => {
const hostId = e.fromHostId; const hostId = e.fromHostId;
//delete e.fromHostId; // delete e.fromHostId;
a[hostId] = a[hostId] || { updated: [], deletedIds: [] }; a[hostId] = a[hostId] || { updated: [], deletedIds: [] };
if (e.deleted) { if (e.deleted) {
a[hostId].deletedIds.push(e.id); a[hostId].deletedIds.push(e.id);
@ -539,21 +535,22 @@ function reducer(previousState, action) {
return a; return a;
}, {}); }, {});
newState = reducer(previousState, { newState = reducer(previousState, {
type: "DEVICES_UPDATE", type: 'DEVICES_UPDATE',
partial: true, partial: true,
devices, devices,
}); });
for (const hostId in hostDevicesMapByHostId) { for (const hostId in hostDevicesMapByHostId) {
if (hostDevicesMapByHostId[hostId].updated.length > 0) if (hostDevicesMapByHostId[hostId].updated.length > 0) {
newState = reducer(newState, { newState = reducer(newState, {
type: "HOST_DEVICES_UPDATE", type: 'HOST_DEVICES_UPDATE',
devices: hostDevicesMapByHostId[hostId].updated, devices: hostDevicesMapByHostId[hostId].updated,
partial: true, partial: true,
hostId, hostId,
}); });
}
if (hostDevicesMapByHostId[hostId].deletedIds.length > 0) { if (hostDevicesMapByHostId[hostId].deletedIds.length > 0) {
newState = reducer(newState, { newState = reducer(newState, {
type: "HOST_DEVICES_DELETE", type: 'HOST_DEVICES_DELETE',
deviceIds: hostDevicesMapByHostId[hostId].deletedIds, deviceIds: hostDevicesMapByHostId[hostId].deletedIds,
partial: true, partial: true,
hostId, hostId,
@ -561,7 +558,7 @@ function reducer(previousState, action) {
} }
} }
break; break;
case "HG_UPDATE": case 'HG_UPDATE':
newState = update(previousState, { newState = update(previousState, {
[action.key]: { $set: action.value }, [action.key]: { $set: action.value },
}); });
@ -581,7 +578,7 @@ const initState = {
}, },
active: { active: {
activeRoom: -1, activeRoom: -1,
activeTab: "Devices", activeTab: 'Devices',
activeScene: -1, activeScene: -1,
activeAutomation: -1, activeAutomation: -1,
activeHost: -1, activeHost: -1,
@ -613,8 +610,8 @@ const initState = {
}; };
function createSmartHutStore() { function createSmartHutStore() {
const token = localStorage.getItem("token"); const token = localStorage.getItem('token');
const exp = localStorage.getItem("exp"); const exp = localStorage.getItem('exp');
const initialState = update(initState, { const initialState = update(initState, {
login: { login: {
@ -624,15 +621,15 @@ function createSmartHutStore() {
}); });
if (!initialState.login.loggedIn) { if (!initialState.login.loggedIn) {
localStorage.removeItem("token"); localStorage.removeItem('token');
localStorage.removeItem("exp"); localStorage.removeItem('exp');
initialState.login.token = null; initialState.login.token = null;
} }
const store = createStore( const store = createStore(
reducer, reducer,
initialState, initialState,
compose(applyMiddleware(thunk), applyMiddleware(reduxWebSocket())) compose(applyMiddleware(thunk), applyMiddleware(reduxWebSocket())),
); );
if (initialState.login.loggedIn) { if (initialState.login.loggedIn) {
store.dispatch(connect(socketURL(token))); store.dispatch(connect(socketURL(token)));

View file

@ -1,134 +1,134 @@
const actions = { const actions = {
loginSuccess: (token) => ({ loginSuccess: (token) => ({
type: "LOGIN_UPDATE", type: 'LOGIN_UPDATE',
login: { login: {
loggedIn: true, loggedIn: true,
token, token,
}, },
}), }),
logout: () => ({ logout: () => ({
type: "LOGOUT", type: 'LOGOUT',
}), }),
userInfoUpdate: (userInfo) => ({ userInfoUpdate: (userInfo) => ({
type: "USER_INFO_UPDATE", type: 'USER_INFO_UPDATE',
userInfo, userInfo,
}), }),
roomSave: (room) => ({ roomSave: (room) => ({
type: "ROOM_SAVE", type: 'ROOM_SAVE',
room, room,
}), }),
sceneSave: (scene) => ({ sceneSave: (scene) => ({
type: "SCENE_SAVE", type: 'SCENE_SAVE',
scene, scene,
}), }),
deviceSave: (device) => ({ deviceSave: (device) => ({
type: "DEVICE_SAVE", type: 'DEVICE_SAVE',
device, device,
}), }),
hostDeviceSave: (hostId, device) => ({ hostDeviceSave: (hostId, device) => ({
type: "HOST_DEVICE_SAVE", type: 'HOST_DEVICE_SAVE',
hostId, hostId,
device, device,
}), }),
triggerSave: (automation) => ({ triggerSave: (automation) => ({
type: "TRIGGER_SAVE", type: 'TRIGGER_SAVE',
automation, automation,
}), }),
scenePrioritySave: (automation) => ({ scenePrioritySave: (automation) => ({
type: "SCENE_PRIORITY_SAVE", type: 'SCENE_PRIORITY_SAVE',
automation, automation,
}), }),
automationSave: (automation) => ({ automationSave: (automation) => ({
type: "AUTOMATION_SAVE", type: 'AUTOMATION_SAVE',
automation, automation,
}), }),
automationsUpdate: (automations) => ({ automationsUpdate: (automations) => ({
type: "AUTOMATION_UPDATE", type: 'AUTOMATION_UPDATE',
automations, automations,
}), }),
stateSave: (sceneState) => ({ stateSave: (sceneState) => ({
type: "STATE_SAVE", type: 'STATE_SAVE',
sceneState, sceneState,
}), }),
statesUpdate: (sceneId, sceneStates) => ({ statesUpdate: (sceneId, sceneStates) => ({
type: "STATES_UPDATE", type: 'STATES_UPDATE',
sceneId, sceneId,
sceneStates, sceneStates,
}), }),
devicesUpdate: (roomId, devices, partial = false) => ({ devicesUpdate: (roomId, devices, partial = false) => ({
type: "DEVICES_UPDATE", type: 'DEVICES_UPDATE',
roomId, roomId,
devices, devices,
partial, partial,
}), }),
hostDevicesUpdate: (hostId, devices, partial = false) => ({ hostDevicesUpdate: (hostId, devices, partial = false) => ({
type: "HOST_DEVICES_UPDATE", type: 'HOST_DEVICES_UPDATE',
hostId, hostId,
partial, partial,
devices, devices,
}), }),
stateDelete: (stateId) => ({ stateDelete: (stateId) => ({
type: "STATE_DELETE", type: 'STATE_DELETE',
stateId, stateId,
}), }),
deviceOperationUpdate: (devices) => ({ deviceOperationUpdate: (devices) => ({
type: "DEVICES_UPDATE", type: 'DEVICES_UPDATE',
devices, devices,
partial: true, partial: true,
}), }),
roomsUpdate: (rooms) => ({ roomsUpdate: (rooms) => ({
type: "ROOMS_UPDATE", type: 'ROOMS_UPDATE',
rooms, rooms,
}), }),
hostRoomsUpdate: (hostId, rooms) => ({ hostRoomsUpdate: (hostId, rooms) => ({
type: "HOST_ROOMS_UPDATE", type: 'HOST_ROOMS_UPDATE',
hostId, hostId,
rooms, rooms,
}), }),
roomDelete: (roomId) => ({ roomDelete: (roomId) => ({
type: "ROOM_DELETE", type: 'ROOM_DELETE',
roomId, roomId,
}), }),
automationDelete: (id) => ({ automationDelete: (id) => ({
type: "AUTOMATION_DELETE", type: 'AUTOMATION_DELETE',
id, id,
}), }),
sceneDelete: (sceneId) => ({ sceneDelete: (sceneId) => ({
type: "SCENE_DELETE", type: 'SCENE_DELETE',
sceneId, sceneId,
}), }),
scenesUpdate: (scenes) => ({ scenesUpdate: (scenes) => ({
type: "SCENES_UPDATE", type: 'SCENES_UPDATE',
scenes, scenes,
}), }),
hostScenesUpdate: (hostId, scenes) => ({ hostScenesUpdate: (hostId, scenes) => ({
type: "HOST_SCENES_UPDATE", type: 'HOST_SCENES_UPDATE',
hostId, hostId,
scenes, scenes,
}), }),
deviceDelete: (deviceId) => ({ deviceDelete: (deviceId) => ({
type: "DEVICE_DELETE", type: 'DEVICE_DELETE',
deviceId, deviceId,
}), }),
hostsUpdate: (hosts) => ({ hostsUpdate: (hosts) => ({
type: "HG_UPDATE", type: 'HG_UPDATE',
key: "hosts", key: 'hosts',
value: hosts, value: hosts,
}), }),
guestsUpdate: (hosts) => ({ guestsUpdate: (hosts) => ({
type: "HG_UPDATE", type: 'HG_UPDATE',
key: "guests", key: 'guests',
value: hosts, value: hosts,
}), }),
getHostDevices: (host) => ({ getHostDevices: (host) => ({
type: "GET_HOST_DEVICES", type: 'GET_HOST_DEVICES',
host, host,
}), }),
guestUpdate: (guests) => ({ guestUpdate: (guests) => ({
type: "HG_UPDATE", type: 'HG_UPDATE',
key: "guests", key: 'guests',
value: guests, value: guests,
}), }),
}; };
@ -136,23 +136,23 @@ const actions = {
export const appActions = { export const appActions = {
// -1 for home view // -1 for home view
setActiveRoom: (activeRoom = -1) => ({ setActiveRoom: (activeRoom = -1) => ({
type: "SET_ACTIVE", type: 'SET_ACTIVE',
key: "activeRoom", key: 'activeRoom',
value: activeRoom, value: activeRoom,
}), }),
setActiveTab: (activeTab) => ({ setActiveTab: (activeTab) => ({
type: "SET_ACTIVE", type: 'SET_ACTIVE',
key: "activeTab", key: 'activeTab',
value: activeTab, value: activeTab,
}), }),
setActiveScene: (activeScene = -1) => ({ setActiveScene: (activeScene = -1) => ({
type: "SET_ACTIVE", type: 'SET_ACTIVE',
key: "activeScene", key: 'activeScene',
value: activeScene, value: activeScene,
}), }),
setActiveHost: (activeHost = -1) => ({ setActiveHost: (activeHost = -1) => ({
type: "SET_ACTIVE", type: 'SET_ACTIVE',
key: "activeHost", key: 'activeHost',
value: activeHost, value: activeHost,
}), }),
}; };

View file

@ -0,0 +1,39 @@
import React, { Component } from 'react';
import {
Image,
Grid,
Button,
Icon,
Header,
Container,
} from 'semantic-ui-react';
export default class Confirm extends Component {
render() {
return (
<>
<Button circular style={{ margin: '2em' }} href="/">
<Icon name="arrow alternate circle left" />
Go Home
{' '}
</Button>
<Grid
textAlign="center"
style={{ height: '70vh' }}
verticalAlign="middle"
>
<Grid.Column style={{ maxWidth: 450 }}>
<Header as="h2" color="blue" textAlign="center">
<Image src="img/logo.png" />
{' '}
Congratulation!
</Header>
<Container textAlign="center">
<p>{this.props.msg}</p>
</Container>
</Grid.Column>
</Grid>
</>
);
}
}

View file

@ -1,40 +1,9 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import { import Confirm from './Confirm';
Image,
Grid,
Button,
Icon,
Header,
Container,
} from "semantic-ui-react";
const msg = "An E-mail has been sent to your address, confirm your registration by following the enclosed link. If you don't find the E-mail please check also the spam folder.";
export default class ConfirmForgotPasswrod extends Component { export default class ConfirmForgotPasswrod extends Component {
render() { render() {
return ( return <Confirm msg={msg} />;
<React.Fragment>
<Button circular style={{ margin: "2em" }} href="/">
<Icon name="arrow alternate circle left" />
Go Home{" "}
</Button>
<Grid
textAlign="center"
style={{ height: "70vh" }}
verticalAlign="middle"
>
<Grid.Column style={{ maxWidth: 450 }}>
<Header as="h2" color="blue" textAlign="center">
<Image src="img/logo.png" /> Link has been sent!
</Header>
<Container textAlign="center">
<p>
An E-mail has been sent to your address, please follow the
instructions to create a new password. If you don't find the
E-mail please check also the spam folder.
</p>
</Container>
</Grid.Column>
</Grid>
</React.Fragment>
);
} }
} }

View file

@ -1,40 +1,9 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import { import Confirm from './Confirm';
Image,
Grid,
Button,
Icon,
Header,
Container,
} from "semantic-ui-react";
const msg = "An E-mail has been sent to your address, confirm your registration by following the enclosed link. If you don't find the E-mail please check also the spam folder.";
export default class ConfirmRegistration extends Component { export default class ConfirmRegistration extends Component {
render() { render() {
return ( return <Confirm msg={msg} />;
<React.Fragment>
<Button circular style={{ margin: "2em" }} href="/">
<Icon name="arrow alternate circle left" />
Go Home{" "}
</Button>
<Grid
textAlign="center"
style={{ height: "70vh" }}
verticalAlign="middle"
>
<Grid.Column style={{ maxWidth: 450 }}>
<Header as="h2" color="blue" textAlign="center">
<Image src="img/logo.png" /> Congratulation!
</Header>
<Container textAlign="center">
<p>
An E-mail has been sent to your address, confirm your
registration by following the enclosed link. If you don't find
the E-mail please check also the spam folder.
</p>
</Container>
</Grid.Column>
</Grid>
</React.Fragment>
);
} }
} }

View file

@ -1,36 +1,10 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import { import Confirm from './Confirm';
Image,
Grid, const msg = 'Your password has been successfully reset.';
Button,
Icon,
Header,
Container,
} from "semantic-ui-react";
export default class ConfirmResetPassword extends Component { export default class ConfirmResetPassword extends Component {
render() { render() {
return ( return <Confirm msg={msg} />;
<React.Fragment>
<Button circular style={{ margin: "2em" }} href="/">
<Icon name="arrow alternate circle left" />
Go Home{" "}
</Button>
<Grid
textAlign="center"
style={{ height: "70vh" }}
verticalAlign="middle"
>
<Grid.Column style={{ maxWidth: 450 }}>
<Header as="h2" color="blue" textAlign="center">
<Image src="img/logo.png" /> Congratulation!
</Header>
<Container textAlign="center">
<p>Your password has been successfully reset.</p>
</Container>
</Grid.Column>
</Grid>
</React.Fragment>
);
} }
} }

View file

@ -1,24 +1,26 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import DevicePanel from "../components/dashboard/DevicePanel"; import {
import ScenesPanel from "../components/dashboard/ScenesPanel"; Grid, Responsive, Button, Menu,
import AutomationsPanel from "../components/dashboard/AutomationsPanel"; } from 'semantic-ui-react';
import HostsPanel from "../components/dashboard/HostsPanel"; import { connect } from 'react-redux';
import Navbar from "./Navbar"; import DevicePanel from '../components/dashboard/DevicePanel';
import ScenesNavbar from "./ScenesNavbar"; import ScenesPanel from '../components/dashboard/ScenesPanel';
import HostsNavbar from "./HostsNavbar"; import AutomationsPanel from '../components/dashboard/AutomationsPanel';
import MyHeader from "../components/HeaderController"; import HostsPanel from '../components/dashboard/HostsPanel';
import { Grid, Responsive, Button, Menu } from "semantic-ui-react"; import Navbar from './Navbar';
import { mobilePanelStyle } from "../components/dashboard/devices/styleComponents"; import ScenesNavbar from './ScenesNavbar';
import HostsNavbar from './HostsNavbar';
import MyHeader from '../components/HeaderController';
import { mobilePanelStyle } from '../components/dashboard/devices/styleComponents';
import { RemoteService } from "../remote"; import { RemoteService } from '../remote';
import { connect } from "react-redux"; import { appActions } from '../storeActions';
import { appActions } from "../storeActions";
class Dashboard extends Component { class Dashboard extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = this.initialState; this.state = this.initialState;
this.activeTab = "Devices"; this.activeTab = 'Devices';
this.selectTab = this.selectTab.bind(this); this.selectTab = this.selectTab.bind(this);
} }
@ -47,13 +49,13 @@ class Dashboard extends Component {
renderTab(tab) { renderTab(tab) {
switch (tab) { switch (tab) {
case "Devices": case 'Devices':
return <DevicePanel tab={this.state.activeTab} />; return <DevicePanel tab={this.state.activeTab} />;
case "Scenes": case 'Scenes':
return <ScenesPanel tab={this.state.activeTab} />; return <ScenesPanel tab={this.state.activeTab} />;
case "Automations": case 'Automations':
return <AutomationsPanel />; return <AutomationsPanel />;
case "Hosts": case 'Hosts':
return <HostsPanel />; return <HostsPanel />;
default: default:
return <h1>ERROR</h1>; return <h1>ERROR</h1>;
@ -62,11 +64,11 @@ class Dashboard extends Component {
renderNavbar(tab) { renderNavbar(tab) {
switch (tab) { switch (tab) {
case "Devices": case 'Devices':
return <Navbar />; return <Navbar />;
case "Scenes": case 'Scenes':
return <ScenesNavbar />; return <ScenesNavbar />;
case "Hosts": case 'Hosts':
return <HostsNavbar />; return <HostsNavbar />;
default: default:
return <h1>ERROR</h1>; return <h1>ERROR</h1>;
@ -74,21 +76,21 @@ class Dashboard extends Component {
} }
get hasNavbar() { get hasNavbar() {
return this.state.activeTab !== "Automations"; return this.state.activeTab !== 'Automations';
} }
render() { render() {
// needed to correctly assign the background image // needed to correctly assign the background image
//in case a room has one. // in case a room has one.
let backgroundImageHelper; let backgroundImageHelper;
if (this.activeTab === "Devices") { if (this.activeTab === 'Devices') {
backgroundImageHelper = this.props.backgroundImage; backgroundImageHelper = this.props.backgroundImage;
} else { } else {
backgroundImageHelper = null; backgroundImageHelper = null;
} }
//console.log("helper is",helper) // console.log("helper is",helper)
return ( return (
<div style={{ background: "#1b1c1d" }}> <div style={{ background: '#1b1c1d' }}>
<Responsive minWidth={768}> <Responsive minWidth={768}>
<Grid> <Grid>
<Grid.Row color="black" style={{ paddingBottom: 0 }}> <Grid.Row color="black" style={{ paddingBottom: 0 }}>
@ -102,25 +104,25 @@ class Dashboard extends Component {
<Menu.Item <Menu.Item
name="Devices" name="Devices"
content="Devices" content="Devices"
active={this.activeTab === "Devices"} active={this.activeTab === 'Devices'}
onClick={this.selectTab} onClick={this.selectTab}
/> />
<Menu.Item <Menu.Item
name="Scenes" name="Scenes"
content="Scenes" content="Scenes"
active={this.activeTab === "Scenes"} active={this.activeTab === 'Scenes'}
onClick={this.selectTab} onClick={this.selectTab}
/> />
<Menu.Item <Menu.Item
name="Automations" name="Automations"
content="Automations" content="Automations"
active={this.activeTab === "Automations"} active={this.activeTab === 'Automations'}
onClick={this.selectTab} onClick={this.selectTab}
/> />
<Menu.Item <Menu.Item
name="Hosts" name="Hosts"
content="Hosts and Guests" content="Hosts and Guests"
active={this.activeTab === "Hosts"} active={this.activeTab === 'Hosts'}
onClick={this.selectTab} onClick={this.selectTab}
/> />
</Menu> </Menu>
@ -134,13 +136,13 @@ class Dashboard extends Component {
<Grid.Column width={this.hasNavbar ? 13 : 16}> <Grid.Column width={this.hasNavbar ? 13 : 16}>
<div <div
style={{ style={{
backgroundImage: "url(" + backgroundImageHelper + ")", backgroundImage: `url(${backgroundImageHelper})`,
backgroundColor: "#fafafa", backgroundColor: '#fafafa',
height: "85vh", height: '85vh',
padding: "0rem 3rem", padding: '0rem 3rem',
color: "#000000", color: '#000000',
overflow: "auto", overflow: 'auto',
maxHeight: "75vh", maxHeight: '75vh',
}} }}
> >
{this.renderTab(this.activeTab)} {this.renderTab(this.activeTab)}
@ -162,33 +164,33 @@ class Dashboard extends Component {
basic basic
name="Devices" name="Devices"
content="Devices" content="Devices"
active={this.activeTab === "Devices"} active={this.activeTab === 'Devices'}
color={this.activeTab === "Devices" ? "yellow" : "grey"} color={this.activeTab === 'Devices' ? 'yellow' : 'grey'}
onClick={this.selectTab} onClick={this.selectTab}
/> />
<Button <Button
basic basic
name="Scenes" name="Scenes"
content="Scenes" content="Scenes"
active={this.activeTab === "Scenes"} active={this.activeTab === 'Scenes'}
color={this.activeTab === "Scenes" ? "yellow" : "grey"} color={this.activeTab === 'Scenes' ? 'yellow' : 'grey'}
onClick={this.selectTab} onClick={this.selectTab}
/> />
<Button <Button
basic basic
name="Automations" name="Automations"
content="Automations" content="Automations"
active={this.activeTab === "Automations"} active={this.activeTab === 'Automations'}
color={this.activeTab === "Automations" ? "yellow" : "grey"} color={this.activeTab === 'Automations' ? 'yellow' : 'grey'}
onClick={this.selectTab} onClick={this.selectTab}
/> />
<Button <Button
basic basic
name="Hosts" name="Hosts"
content="Hosts" content="Hosts"
active={this.activeTab === "Hosts"} active={this.activeTab === 'Hosts'}
color={ color={
this.activeTab === "Hosts and Guests" ? "yellow" : "grey" this.activeTab === 'Hosts and Guests' ? 'yellow' : 'grey'
} }
onClick={this.selectTab} onClick={this.selectTab}
/> />
@ -223,15 +225,12 @@ const mapStateToProps = (state, _) => ({
get backgroundImage() { get backgroundImage() {
if (state.active.activeRoom === -1) { if (state.active.activeRoom === -1) {
return null; return null;
} else {
return state.rooms[state.active.activeRoom].image;
} }
return state.rooms[state.active.activeRoom].image;
}, },
}); });
const setActiveTab = (activeTab) => { const setActiveTab = (activeTab) => (dispatch) => dispatch(appActions.setActiveTab(activeTab));
return (dispatch) => dispatch(appActions.setActiveTab(activeTab));
};
const DashboardContainer = connect(mapStateToProps, { const DashboardContainer = connect(mapStateToProps, {
...RemoteService, ...RemoteService,

View file

@ -1,5 +1,5 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import { Button } from "semantic-ui-react"; import { Button } from 'semantic-ui-react';
export default class Dashboard extends Component { export default class Dashboard extends Component {
handleLogOut = (e) => { handleLogOut = (e) => {
@ -9,8 +9,9 @@ export default class Dashboard extends Component {
render() { render() {
return ( return (
<Button circular style={{ margin: "2em" }} onClick={this.handleLogOut}> <Button circular style={{ margin: '2em' }} onClick={this.handleLogOut}>
Go Home{" "} Go Home
{' '}
</Button> </Button>
); );
} }

View file

@ -1,114 +0,0 @@
import React, { Component } from "react";
import {
Button,
Form,
Grid,
Header,
Image,
Icon,
Message,
} from "semantic-ui-react";
import { Redirect } from "react-router-dom";
import { Forms } from "../remote";
export default class ChangePass extends Component {
constructor(props) {
super(props);
this.state = {
password: "",
error: {
state: false,
message: "",
},
success: false,
};
this.handleChangePassword = this.handleChangePassword.bind(this);
}
onChangeHandler = (event) => {
let nam = event.target.name;
let val = event.target.value;
this.setState({ [nam]: val });
};
handleChangePassword = (e) => {
if (this.state.confirmPassword !== this.state.password) {
this.setState({
error: {
state: true,
message: "Passwords do not match.",
},
});
}
Forms.submitResetPassword(this.props.query.token, this.state.password)
.then(() => this.setState({ success: true }))
.catch((err) =>
this.setState({
error: { state: true, message: err.messages.join(" - ") },
})
);
};
render() {
if (this.state.success) {
return <Redirect to="/login" />;
}
return (
<React.Fragment>
<Button circular style={{ margin: "2em" }} href="/">
<Icon name="arrow alternate circle left" />
Go Home{" "}
</Button>
<Grid
textAlign="center"
style={{ height: "70vh" }}
verticalAlign="middle"
>
<Grid.Column style={{ maxWidth: 450 }}>
<Header as="h2" color="blue" textAlign="center">
<Image src="img/logo.png" /> Reset Password
</Header>
<Form
size="large"
style={{ marginTop: "2em" }}
error={this.state.error.state}
>
<Message
error
header="Change Password Error"
content={this.state.error.message}
/>
<Form.Input
icon="address card outline"
iconPosition="left"
placeholder="Reset your password"
name="password"
type="password"
onChange={this.onChangeHandler}
required
/>
<Form.Input
icon="address card outline"
iconPosition="left"
placeholder="Confirm Password"
name="confirmPassword"
type="password"
onChange={this.onChangeHandler}
required
/>
<Button
color="blue"
fluid
size="large"
onClick={this.handleChangePassword}
>
Confirm password
</Button>
</Form>
</Grid.Column>
</Grid>
</React.Fragment>
);
}
}

View file

@ -1,4 +1,4 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import { import {
Button, Button,
Form, Form,
@ -7,26 +7,28 @@ import {
Image, Image,
Icon, Icon,
Message, Message,
} from "semantic-ui-react"; } from 'semantic-ui-react';
import { Redirect } from "react-router-dom"; import { Redirect } from 'react-router-dom';
import { Forms } from "../remote"; import { Forms } from '../remote';
export default class ForgotPass extends Component { export default class ForgotPass extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
user: "", user: '',
error: { error: {
state: false, state: false,
message: [], message: [],
}, },
success: false, success: false,
}; };
this.handleChangePassword = this.handleChangePassword.bind(this);
} }
onChangeHandler = (event) => { onChangeHandler = (event) => {
let nam = event.target.name; const nam = event.target.name;
let val = event.target.value; const val = event.target.value;
this.setState({ [nam]: val }); this.setState({ [nam]: val });
}; };
@ -35,33 +37,52 @@ export default class ForgotPass extends Component {
Forms.submitInitResetPassword(this.state.user) Forms.submitInitResetPassword(this.state.user)
.then(() => this.setState({ success: true })) .then(() => this.setState({ success: true }))
.catch((err) => .catch((err) => this.setState({ error: { state: true, message: err.messages } }));
this.setState({ error: { state: true, message: err.messages } }) };
);
handleChangePassword = (e) => {
if (this.state.confirmPassword !== this.state.password) {
this.setState({
error: {
state: true,
message: 'Passwords do not match.',
},
});
}
Forms.submitResetPassword(this.props.query.token, this.state.password)
.then(() => this.setState({ success: true }))
.catch((err) => this.setState({
error: { state: true, message: err.messages.join(' - ') },
}));
}; };
render() { render() {
console.log(this.props);
if (this.state.success) { if (this.state.success) {
return <Redirect to="sent-email" />; return <Redirect to="sent-email" />;
} }
return ( return (
<React.Fragment> <>
<Button circular style={{ margin: "2em" }} href="/"> <Button circular style={{ margin: '2em' }} href="/">
<Icon name="arrow alternate circle left" /> <Icon name="arrow alternate circle left" />
Go Home{" "} Go Home
{' '}
</Button> </Button>
<Grid <Grid
textAlign="center" textAlign="center"
style={{ height: "70vh" }} style={{ height: '70vh' }}
verticalAlign="middle" verticalAlign="middle"
> >
<Grid.Column style={{ maxWidth: 450 }}> <Grid.Column style={{ maxWidth: 450 }}>
<Header as="h2" color="blue" textAlign="center"> <Header as="h2" color="blue" textAlign="center">
<Image src="img/logo.png" /> Reset Password <Image src="img/logo.png" />
{' '}
Reset Password
</Header> </Header>
<Form <Form
size="large" size="large"
style={{ marginTop: "2em" }} style={{ marginTop: '2em' }}
error={this.state.error.state} error={this.state.error.state}
> >
<Message error> <Message error>
@ -74,6 +95,8 @@ export default class ForgotPass extends Component {
</span> </span>
))} ))}
</Message> </Message>
{this.props.type === 'FPassword1' ? (
<>
<Form.Input <Form.Input
icon="address card outline" icon="address card outline"
iconPosition="left" iconPosition="left"
@ -91,10 +114,41 @@ export default class ForgotPass extends Component {
> >
Send E-mail Send E-mail
</Button> </Button>
</>
) : (
<>
<Form.Input
icon="address card outline"
iconPosition="left"
placeholder="Reset your password"
name="password"
type="password"
onChange={this.onChangeHandler}
required
/>
<Form.Input
icon="address card outline"
iconPosition="left"
placeholder="Confirm Password"
name="confirmPassword"
type="password"
onChange={this.onChangeHandler}
required
/>
<Button
color="blue"
fluid
size="large"
onClick={this.handleChangePassword}
>
Confirm password
</Button>
</>
)}
</Form> </Form>
</Grid.Column> </Grid.Column>
</Grid> </Grid>
</React.Fragment> </>
); );
} }
} }

View file

@ -1,91 +0,0 @@
import React, { Component } from "react";
import { render } from "react-dom";
import HomeNavbar from "./../components/HomeNavbar";
import {
Container,
Icon,
Image,
Menu,
Sidebar,
Responsive,
Header,
Divider,
Message,
Grid,
} from "semantic-ui-react";
class Paragraph extends Component {
state = { visible: true };
handleDismiss = () => {
this.setState({ visible: false });
setTimeout(() => {
this.setState({ visible: true });
}, 2000);
};
render() {
if (this.state.visible) {
return (
<Message
onDismiss={this.handleDismiss}
header="Link has been sent!"
content="An e-mail has been sent your address, please follow the
instruction to create a new password"
/>
);
}
return (
<p>
<br />
<i>The message will return in 2s</i>
<br />
<br />
</p>
);
}
}
const MessageReg = () => (
<Grid>
<HomeNavbar />
<Divider />
<Grid.Row height={3}></Grid.Row>
<Grid.Row height={3}></Grid.Row>
<Grid.Row height={3}></Grid.Row>
<Grid.Row height={3}>
<Grid.Column width={6}></Grid.Column>
<Grid.Column width={10}>
<Image src="title5.png" />
</Grid.Column>
<Grid.Column width={3}></Grid.Column>
</Grid.Row>
<Grid.Row height={3}></Grid.Row>
<Grid.Row height={3}></Grid.Row>
<Grid.Column width={3}></Grid.Column>
<Grid.Column width={4}>
<Image src="./img/logo.png" />
</Grid.Column>
<Grid.Column width={6}>
<Paragraph />
</Grid.Column>
<Grid.Column width={4}></Grid.Column>
<Grid.Row height={3}></Grid.Row>
<Grid.Row height={3}></Grid.Row>
<Grid.Row height={3}>
<Grid.Column width={3}></Grid.Column>
<Grid.Column width={10}>
<Divider />
</Grid.Column>
<Grid.Column width={3}></Grid.Column>
</Grid.Row>
</Grid>
);
export default class ForgotPasswrod extends React.Component {
render() {
return <MessageReg />;
}
}

View file

@ -1,35 +1,37 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import { Grid, Button, Segment, Responsive, Image } from "semantic-ui-react"; import {
import { Link } from "react-router-dom"; Grid, Button, Segment, Responsive, Image,
import MyHeader from "../components/HeaderController"; } from 'semantic-ui-react';
import { Link } from 'react-router-dom';
import MyHeader from '../components/HeaderController';
export default class FourOhFour extends Component { export default class FourOhFour extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
const meme = [ const meme = [
"1.jpeg", '1.jpeg',
"2.jpeg", '2.jpeg',
"3.png", '3.png',
"4.jpeg", '4.jpeg',
"5.jpeg", '5.jpeg',
"6.jpg", '6.jpg',
"7.jpg", '7.jpg',
"8.jpg", '8.jpg',
"9.jpeg", '9.jpeg',
"10.jpg", '10.jpg',
"11.jpeg", '11.jpeg',
"12.gif", '12.gif',
"13.gif", '13.gif',
"14.gif", '14.gif',
]; ];
var arrayNum = Math.floor(Math.random() * 13) + 1; const arrayNum = Math.floor(Math.random() * 13) + 1;
var path = "img/room_404_meme/" + meme[arrayNum]; const path = `img/room_404_meme/${meme[arrayNum]}`;
this.state = { meme: path }; this.state = { meme: path };
} }
render() { render() {
return ( return (
<div style={{ height: "110vh", background: "#1b1c1d" }}> <div style={{ height: '110vh', background: '#1b1c1d' }}>
<Responsive minWidth={768}> <Responsive minWidth={768}>
<Grid> <Grid>
<Grid.Row color="black"> <Grid.Row color="black">
@ -59,7 +61,7 @@ export default class FourOhFour extends Component {
our main room! ...or refresh this page some times... our main room! ...or refresh this page some times...
</p> </p>
<Button fluid inverted color="white"> <Button fluid inverted color="white">
<Link style={{ color: "black" }} to="/"> <Link style={{ color: 'black' }} to="/">
Let's go back to our main room! Let's go back to our main room!
</Link> </Link>
</Button> </Button>
@ -103,7 +105,7 @@ export default class FourOhFour extends Component {
our main room! ...or refresh this page some times... our main room! ...or refresh this page some times...
</p> </p>
<Button fluid inverted color="white"> <Button fluid inverted color="white">
<Link style={{ color: "black" }} to="/"> <Link style={{ color: 'black' }} to="/">
Let's go back to our main room! Let's go back to our main room!
</Link> </Link>
</Button> </Button>

View file

@ -1,6 +1,5 @@
import PropTypes from "prop-types"; import PropTypes from 'prop-types';
import React, { Component } from "react"; import React, { Component } from 'react';
import HomeNavbar from "./../components/HomeNavbar";
import { import {
Button, Button,
Container, Container,
@ -14,13 +13,14 @@ import {
Segment, Segment,
Sidebar, Sidebar,
Visibility, Visibility,
} from "semantic-ui-react"; } from 'semantic-ui-react';
import HomeNavbar from '../components/HomeNavbar';
// Heads up! // Heads up!
// We using React Static to prerender our docs with server side rendering, this is a quite simple solution. // We using React Static to prerender our docs with server side rendering, this is a quite simple solution.
// For more advanced usage please check Responsive docs under the "Usage" section. // For more advanced usage please check Responsive docs under the "Usage" section.
const getWidth = () => { const getWidth = () => {
const isSSR = typeof window === "undefined"; const isSSR = typeof window === 'undefined';
return isSSR ? Responsive.onlyTablet.minWidth : window.innerWidth; return isSSR ? Responsive.onlyTablet.minWidth : window.innerWidth;
}; };
@ -35,10 +35,10 @@ const HomepageHeading = ({ mobile }) => (
content="SmartHut" content="SmartHut"
inverted inverted
style={{ style={{
fontSize: mobile ? "2em" : "4em", fontSize: mobile ? '2em' : '4em',
fontWeight: "normal", fontWeight: 'normal',
marginBottom: 0, marginBottom: 0,
marginTop: mobile ? "1.5em" : "3em", marginTop: mobile ? '1.5em' : '3em',
}} }}
/> />
<Header <Header
@ -46,9 +46,9 @@ const HomepageHeading = ({ mobile }) => (
content="Keep your Home fully Connected" content="Keep your Home fully Connected"
inverted inverted
style={{ style={{
fontSize: mobile ? "1.5em" : "1.7em", fontSize: mobile ? '1.5em' : '1.7em',
fontWeight: "normal", fontWeight: 'normal',
marginTop: mobile ? "0.5em" : "1.5em", marginTop: mobile ? '0.5em' : '1.5em',
}} }}
/> />
<Button size="huge" color="orange" href="/signup"> <Button size="huge" color="orange" href="/signup">
@ -64,7 +64,9 @@ HomepageHeading.propTypes = {
class DesktopContainer extends Component { class DesktopContainer extends Component {
state = {}; state = {};
hideFixedMenu = () => this.setState({ fixed: false }); hideFixedMenu = () => this.setState({ fixed: false });
showFixedMenu = () => this.setState({ fixed: true }); showFixedMenu = () => this.setState({ fixed: true });
render() { render() {
@ -82,9 +84,9 @@ class DesktopContainer extends Component {
textAlign="center" textAlign="center"
style={{ style={{
minHeight: 700, minHeight: 700,
padding: "1em 0em", padding: '1em 0em',
background: ` linear-gradient(to bottom, rgba(0, 46, 200, 0.51), rgba(0, 0, 0, 0.51)), url("img/header.jpg")`, background: ' linear-gradient(to bottom, rgba(0, 46, 200, 0.51), rgba(0, 0, 0, 0.51)), url("img/header.jpg")',
backgroundSize: "cover", backgroundSize: 'cover',
}} }}
vertical vertical
> >
@ -139,7 +141,7 @@ class MobileContainer extends Component {
<Segment <Segment
inverted inverted
textAlign="center" textAlign="center"
style={{ minHeight: 350, padding: "1em 0em" }} style={{ minHeight: 350, padding: '1em 0em' }}
vertical vertical
> >
<Container> <Container>
@ -151,7 +153,7 @@ class MobileContainer extends Component {
<Button as="a" inverted> <Button as="a" inverted>
Log in Log in
</Button> </Button>
<Button as="a" inverted style={{ marginLeft: "0.5em" }}> <Button as="a" inverted style={{ marginLeft: '0.5em' }}>
Sign Up Sign Up
</Button> </Button>
</Menu.Item> </Menu.Item>
@ -184,20 +186,20 @@ ResponsiveContainer.propTypes = {
const Home = () => ( const Home = () => (
<ResponsiveContainer> <ResponsiveContainer>
<Segment style={{ padding: "8em 0em" }} vertical> <Segment style={{ padding: '8em 0em' }} vertical>
<Grid container stackable verticalAlign="middle"> <Grid container stackable verticalAlign="middle">
<Grid.Row> <Grid.Row>
<Grid.Column width={8}> <Grid.Column width={8}>
<Header as="h3" style={{ fontSize: "2em" }}> <Header as="h3" style={{ fontSize: '2em' }}>
We help you keep your home connected We help you keep your home connected
</Header> </Header>
<p style={{ fontSize: "1.33em" }}> <p style={{ fontSize: '1.33em' }}>
In a few steps your home will be fully connected with SmartHut. In a few steps your home will be fully connected with SmartHut.
</p> </p>
<Header as="h3" style={{ fontSize: "2em" }}> <Header as="h3" style={{ fontSize: '2em' }}>
Choose between a wide range of devices Choose between a wide range of devices
</Header> </Header>
<p style={{ fontSize: "1.33em" }}> <p style={{ fontSize: '1.33em' }}>
SmartHut is a leading worldwide company in technology innovation. SmartHut is a leading worldwide company in technology innovation.
Explore our website to find the best devices for each room of your Explore our website to find the best devices for each room of your
home! home!
@ -210,12 +212,12 @@ const Home = () => (
</Grid> </Grid>
</Segment> </Segment>
<Segment style={{ padding: "8em 0em" }} vertical> <Segment style={{ padding: '8em 0em' }} vertical>
<Container text> <Container text>
<Header as="h3" style={{ fontSize: "2em" }}> <Header as="h3" style={{ fontSize: '2em' }}>
Have you ever dreamt about a smart home? Have you ever dreamt about a smart home?
</Header> </Header>
<p style={{ fontSize: "1.33em" }}> <p style={{ fontSize: '1.33em' }}>
Let us carrying you into the future. With SmartHut, being at home will Let us carrying you into the future. With SmartHut, being at home will
be a refreshing experience. With just a few clicks, you will be able be a refreshing experience. With just a few clicks, you will be able
the set the illumination of your entire place. Follow the intelligent the set the illumination of your entire place. Follow the intelligent
@ -224,7 +226,7 @@ const Home = () => (
</Container> </Container>
</Segment> </Segment>
<Segment inverted vertical style={{ padding: "5em 0em" }}> <Segment inverted vertical style={{ padding: '5em 0em' }}>
<Container> <Container>
<Grid divided inverted stackable> <Grid divided inverted stackable>
<Grid.Row> <Grid.Row>

View file

@ -1,9 +1,11 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import { Menu, Grid, Responsive, Dropdown } from "semantic-ui-react"; import {
import HostModal from "../components/HostModal"; Menu, Grid, Responsive, Dropdown,
import { RemoteService } from "../remote"; } from 'semantic-ui-react';
import { connect } from "react-redux"; import { connect } from 'react-redux';
import { appActions } from "../storeActions"; import HostModal from '../components/HostModal';
import { RemoteService } from '../remote';
import { appActions } from '../storeActions';
class HostsNavbar extends Component { class HostsNavbar extends Component {
constructor(props) { constructor(props) {
@ -29,7 +31,7 @@ class HostsNavbar extends Component {
} }
get activeItemHostsName() { get activeItemHostsName() {
if (this.props.activeItem === -1) return "Home"; if (this.props.activeItem === -1) return 'Home';
return this.props.hosts[this.props.activeHost].name; return this.props.hosts[this.props.activeHost].name;
} }
@ -37,7 +39,7 @@ class HostsNavbar extends Component {
return ( return (
<div> <div>
<Responsive minWidth={768}> <Responsive minWidth={768}>
<Grid style={{ margin: "1em -1em 0 1em" }}> <Grid style={{ margin: '1em -1em 0 1em' }}>
<Grid.Row> <Grid.Row>
<Menu inverted fluid vertical> <Menu inverted fluid vertical>
<Menu.Item <Menu.Item
@ -49,8 +51,7 @@ class HostsNavbar extends Component {
> >
<strong>Hosts</strong> <strong>Hosts</strong>
</Menu.Item> </Menu.Item>
{Object.values(this.props.hosts).map((e, i) => { {Object.values(this.props.hosts).map((e, i) => (
return (
<Menu.Item <Menu.Item
id={e.id} id={e.id}
key={i} key={i}
@ -60,8 +61,7 @@ class HostsNavbar extends Component {
> >
{e.name} {e.name}
</Menu.Item> </Menu.Item>
); ))}
})}
<Menu.Item name="newM"> <Menu.Item name="newM">
<HostModal /> <HostModal />
</Menu.Item> </Menu.Item>
@ -84,8 +84,7 @@ class HostsNavbar extends Component {
<strong>Hosts</strong> <strong>Hosts</strong>
</Dropdown.Item> </Dropdown.Item>
{Object.values(this.props.hosts).map((e, i) => { {Object.values(this.props.hosts).map((e, i) => (
return (
<Menu.Item <Menu.Item
id={e.id} id={e.id}
key={i} key={i}
@ -95,8 +94,7 @@ class HostsNavbar extends Component {
> >
{e.name} {e.name}
</Menu.Item> </Menu.Item>
); ))}
})}
</Dropdown.Menu> </Dropdown.Menu>
</Dropdown> </Dropdown>
</Menu> </Menu>
@ -106,9 +104,7 @@ class HostsNavbar extends Component {
} }
} }
const setActiveHost = (activeHost) => { const setActiveHost = (activeHost) => (dispatch) => dispatch(appActions.setActiveHost(activeHost));
return (dispatch) => dispatch(appActions.setActiveHost(activeHost));
};
const mapStateToProps = (state, _) => ({ const mapStateToProps = (state, _) => ({
hosts: state.hosts, hosts: state.hosts,

View file

@ -1,6 +1,8 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import HomeNavbar from "./../components/HomeNavbar"; import {
import { Container, Header, Divider, Grid } from "semantic-ui-react"; Container, Header, Divider, Grid,
} from 'semantic-ui-react';
import HomeNavbar from '../components/HomeNavbar';
const ContainerExampleAlignment = () => ( const ContainerExampleAlignment = () => (
<div> <div>

View file

@ -1,4 +1,4 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import { import {
Button, Button,
Form, Form,
@ -8,19 +8,19 @@ import {
Message, Message,
Icon, Icon,
Input, Input,
} from "semantic-ui-react"; } from 'semantic-ui-react';
import { RemoteService } from "../remote"; import { withRouter } from 'react-router-dom';
import { withRouter } from "react-router-dom"; import { connect } from 'react-redux';
import { connect } from "react-redux"; import { RemoteService } from '../remote';
class Login extends Component { class Login extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
user: "", user: '',
password: "", password: '',
fireRedirect: false, fireRedirect: false,
error: { state: false, message: "" }, error: { state: false, message: '' },
}; };
} }
@ -29,42 +29,43 @@ class Login extends Component {
this.props this.props
.login(this.state.user, this.state.password) .login(this.state.user, this.state.password)
.then(() => this.props.history.push("/dashboard")) .then(() => this.props.history.push('/dashboard'))
.catch((err) => { .catch((err) => {
this.setState({ this.setState({
error: { state: true, message: err.messages.join(" - ") }, error: { state: true, message: err.messages.join(' - ') },
}); });
}); });
}; };
onChangeHandler = (event) => { onChangeHandler = (event) => {
let nam = event.target.name; const nam = event.target.name;
let val = event.target.value; const val = event.target.value;
this.setState({ [nam]: val }); this.setState({ [nam]: val });
}; };
toggle = () => toggle = () => this.setState((prevState) => ({ rememberme: !prevState.rememberme }));
this.setState((prevState) => ({ rememberme: !prevState.rememberme }));
render() { render() {
return ( return (
<React.Fragment> <>
<Button circular style={{ margin: "2em" }} href="/"> <Button circular style={{ margin: '2em' }} href="/">
<Icon name="arrow alternate circle left" /> <Icon name="arrow alternate circle left" />
Go Home Go Home
</Button> </Button>
<Grid <Grid
textAlign="center" textAlign="center"
style={{ height: "70vh" }} style={{ height: '70vh' }}
verticalAlign="middle" verticalAlign="middle"
> >
<Grid.Column style={{ maxWidth: 450 }}> <Grid.Column style={{ maxWidth: 450 }}>
<Header as="h2" color="blue" textAlign="center"> <Header as="h2" color="blue" textAlign="center">
<Image src="img/logo.png" /> Log-in to SmartHut <Image src="img/logo.png" />
{' '}
Log-in to SmartHut
</Header> </Header>
<Form <Form
size="large" size="large"
style={{ marginTop: "2em" }} style={{ marginTop: '2em' }}
error={this.state.error.state} error={this.state.error.state}
> >
<Message <Message
@ -104,12 +105,14 @@ class Login extends Component {
<a href="/forgot-password">Forgot Password?</a> <a href="/forgot-password">Forgot Password?</a>
</p> </p>
<p> <p>
New to us? <a href="/signup"> Sign Up</a> New to us?
{' '}
<a href="/signup"> Sign Up</a>
</p> </p>
</Message> </Message>
</Grid.Column> </Grid.Column>
</Grid> </Grid>
</React.Fragment> </>
); );
} }
} }
@ -117,6 +120,6 @@ class Login extends Component {
const mapStateToProps = (state, _) => ({ loggedIn: state.login.loggedIn }); const mapStateToProps = (state, _) => ({ loggedIn: state.login.loggedIn });
const LoginContainer = connect( const LoginContainer = connect(
mapStateToProps, mapStateToProps,
RemoteService RemoteService,
)(withRouter(Login)); )(withRouter(Login));
export default LoginContainer; export default LoginContainer;

View file

@ -1,4 +1,4 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import { import {
Menu, Menu,
Button, Button,
@ -6,12 +6,12 @@ import {
Icon, Icon,
Responsive, Responsive,
Dropdown, Dropdown,
} from "semantic-ui-react"; } from 'semantic-ui-react';
import { editButtonStyle } from "../components/dashboard/devices/styleComponents"; import { connect } from 'react-redux';
import RoomModal from "../components/RoomModal"; import { editButtonStyle } from '../components/dashboard/devices/styleComponents';
import { RemoteService } from "../remote"; import RoomModal from '../components/RoomModal';
import { connect } from "react-redux"; import { RemoteService } from '../remote';
import { appActions } from "../storeActions"; import { appActions } from '../storeActions';
class Navbar extends Component { class Navbar extends Component {
constructor(props) { constructor(props) {
@ -40,7 +40,7 @@ class Navbar extends Component {
} }
get activeItemName() { get activeItemName() {
if (this.props.activeRoom === -1) return "Home"; if (this.props.activeRoom === -1) return 'Home';
return this.props.rooms[this.props.activeRoom].name; return this.props.rooms[this.props.activeRoom].name;
} }
@ -62,7 +62,7 @@ class Navbar extends Component {
return ( return (
<div> <div>
<Responsive minWidth={768}> <Responsive minWidth={768}>
<Grid style={{ margin: "1em -1em 0 1em" }}> <Grid style={{ margin: '1em -1em 0 1em' }}>
<Grid.Row color="black"> <Grid.Row color="black">
<button style={editButtonStyle} onClick={this.toggleEditMode}> <button style={editButtonStyle} onClick={this.toggleEditMode}>
Edit Edit
@ -89,8 +89,7 @@ class Navbar extends Component {
</Grid> </Grid>
</Menu.Item> </Menu.Item>
{Object.values(this.props.rooms).map((e, i) => { {Object.values(this.props.rooms).map((e, i) => (
return (
<Menu.Item <Menu.Item
id={e.id} id={e.id}
key={i} key={i}
@ -112,8 +111,7 @@ class Navbar extends Component {
</Grid.Row> </Grid.Row>
</Grid> </Grid>
</Menu.Item> </Menu.Item>
); ))}
})}
<Menu.Item name="newM"> <Menu.Item name="newM">
<Grid> <Grid>
@ -148,8 +146,7 @@ class Navbar extends Component {
</Grid> </Grid>
</Dropdown.Item> </Dropdown.Item>
{Object.values(this.props.rooms).map((e, i) => { {Object.values(this.props.rooms).map((e, i) => (
return (
<Dropdown.Item <Dropdown.Item
id={e.id} id={e.id}
key={i} key={i}
@ -167,12 +164,11 @@ class Navbar extends Component {
</Grid> </Grid>
<RoomModal <RoomModal
ref={this.props.roomModalRefs[e.id]} ref={this.props.roomModalRefs[e.id]}
nicolaStop={true} nicolaStop
id={e.id} id={e.id}
/> />
</Dropdown.Item> </Dropdown.Item>
); ))}
})}
</Dropdown.Menu> </Dropdown.Menu>
</Dropdown> </Dropdown>
</Menu> </Menu>
@ -202,16 +198,14 @@ class Navbar extends Component {
} }
} }
const setActiveRoom = (activeRoom) => { const setActiveRoom = (activeRoom) => (dispatch) => dispatch(appActions.setActiveRoom(activeRoom));
return (dispatch) => dispatch(appActions.setActiveRoom(activeRoom));
};
const mapStateToProps = (state, _) => ({ const mapStateToProps = (state, _) => ({
rooms: state.rooms, rooms: state.rooms,
activeRoom: state.active.activeRoom, activeRoom: state.active.activeRoom,
roomModalRefs: Object.keys(state.rooms).reduce( roomModalRefs: Object.keys(state.rooms).reduce(
(acc, key) => ({ ...acc, [key]: React.createRef() }), (acc, key) => ({ ...acc, [key]: React.createRef() }),
{} {},
), ),
}); });
const NavbarContainer = connect(mapStateToProps, { const NavbarContainer = connect(mapStateToProps, {

View file

@ -1,4 +1,4 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import { import {
Menu, Menu,
Button, Button,
@ -6,12 +6,12 @@ import {
Grid, Grid,
Responsive, Responsive,
Dropdown, Dropdown,
} from "semantic-ui-react"; } from 'semantic-ui-react';
import { editButtonStyle } from "../components/dashboard/devices/styleComponents"; import { connect } from 'react-redux';
import SceneModal from "../components/SceneModal"; import { editButtonStyle } from '../components/dashboard/devices/styleComponents';
import { RemoteService } from "../remote"; import SceneModal from '../components/SceneModal';
import { connect } from "react-redux"; import { RemoteService } from '../remote';
import { appActions } from "../storeActions"; import { appActions } from '../storeActions';
class ScenesNavbar extends Component { class ScenesNavbar extends Component {
constructor(props) { constructor(props) {
@ -35,7 +35,7 @@ class ScenesNavbar extends Component {
} }
get activeItemSceneName() { get activeItemSceneName() {
if (this.props.activeScene === -1) return "Scene"; if (this.props.activeScene === -1) return 'Scene';
return this.props.scenes[this.props.activeScene].name; return this.props.scenes[this.props.activeScene].name;
} }
@ -44,7 +44,7 @@ class ScenesNavbar extends Component {
} }
openCurrentModalMobile() { openCurrentModalMobile() {
//console.log(this.activeItemScene, this.props.sceneModalRefs); // console.log(this.activeItemScene, this.props.sceneModalRefs);
const currentModal = this.props.sceneModalRefs[this.activeItemScene] const currentModal = this.props.sceneModalRefs[this.activeItemScene]
.current; .current;
currentModal.openModal(); currentModal.openModal();
@ -63,7 +63,7 @@ class ScenesNavbar extends Component {
if (sceneId) { if (sceneId) {
this.props this.props
.fetchStates(sceneId) .fetchStates(sceneId)
.catch((err) => console.error(`error fetching states:`, err)); .catch((err) => console.error('error fetching states:', err));
} }
} }
@ -71,7 +71,7 @@ class ScenesNavbar extends Component {
return ( return (
<div> <div>
<Responsive minWidth={768}> <Responsive minWidth={768}>
<Grid style={{ margin: "1em -1em 0 1em" }}> <Grid style={{ margin: '1em -1em 0 1em' }}>
<Grid.Row color="black"> <Grid.Row color="black">
<button style={editButtonStyle} onClick={this.toggleEditMode}> <button style={editButtonStyle} onClick={this.toggleEditMode}>
Edit Edit
@ -89,8 +89,7 @@ class ScenesNavbar extends Component {
<strong>Scenes</strong> <strong>Scenes</strong>
</Menu.Item> </Menu.Item>
{Object.values(this.props.scenes).map((e, i) => { {Object.values(this.props.scenes).map((e, i) => (
return (
<Menu.Item <Menu.Item
id={e.id} id={e.id}
key={i} key={i}
@ -112,8 +111,7 @@ class ScenesNavbar extends Component {
</Grid.Row> </Grid.Row>
</Grid> </Grid>
</Menu.Item> </Menu.Item>
); ))}
})}
<Menu.Item name="newM"> <Menu.Item name="newM">
<Grid> <Grid>
@ -145,8 +143,7 @@ class ScenesNavbar extends Component {
</Grid> </Grid>
</Dropdown.Item> </Dropdown.Item>
{Object.values(this.props.scenes).map((e, i) => { {Object.values(this.props.scenes).map((e, i) => (
return (
<Dropdown.Item <Dropdown.Item
id={e.id} id={e.id}
key={i} key={i}
@ -161,12 +158,11 @@ class ScenesNavbar extends Component {
</Grid> </Grid>
<SceneModal <SceneModal
ref={this.props.sceneModalRefs[e.id]} ref={this.props.sceneModalRefs[e.id]}
nicolaStop={true} nicolaStop
id={e.id} id={e.id}
/> />
</Dropdown.Item> </Dropdown.Item>
); ))}
})}
</Dropdown.Menu> </Dropdown.Menu>
</Dropdown> </Dropdown>
</Menu> </Menu>
@ -196,15 +192,13 @@ class ScenesNavbar extends Component {
} }
} }
const setActiveScene = (activeScene) => { const setActiveScene = (activeScene) => (dispatch) => dispatch(appActions.setActiveScene(activeScene));
return (dispatch) => dispatch(appActions.setActiveScene(activeScene));
};
const mapStateToProps = (state, _) => ({ const mapStateToProps = (state, _) => ({
scenes: state.scenes, scenes: state.scenes,
sceneModalRefs: Object.keys(state.scenes).reduce( sceneModalRefs: Object.keys(state.scenes).reduce(
(acc, key) => ({ ...acc, [key]: React.createRef() }), (acc, key) => ({ ...acc, [key]: React.createRef() }),
{} {},
), ),
get isActiveDefaultScene() { get isActiveDefaultScene() {
return state.active.activeScene === -1; return state.active.activeScene === -1;

View file

@ -1,4 +1,4 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import { import {
Button, Button,
Form, Form,
@ -8,18 +8,18 @@ import {
Icon, Icon,
Input, Input,
Message, Message,
} from "semantic-ui-react"; } from 'semantic-ui-react';
import { Redirect } from "react-router-dom"; import { Redirect } from 'react-router-dom';
import { Forms } from "../remote"; import { Forms } from '../remote';
export default class Signup extends Component { export default class Signup extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
completeName: "", completeName: '',
username: "", username: '',
email: "", email: '',
password: "", password: '',
error: { state: false, message: [] }, error: { state: false, message: [] },
success: false, success: false,
}; };
@ -36,14 +36,12 @@ export default class Signup extends Component {
Forms.submitRegistration(params) Forms.submitRegistration(params)
.then(() => this.setState({ success: true })) .then(() => this.setState({ success: true }))
.catch((err) => .catch((err) => this.setState({ error: { state: true, message: err.messages } }));
this.setState({ error: { state: true, message: err.messages } })
);
}; };
onChangeHandler = (event) => { onChangeHandler = (event) => {
let nam = event.target.name; const nam = event.target.name;
let val = event.target.value; const val = event.target.value;
this.setState({ [nam]: val }); this.setState({ [nam]: val });
}; };
@ -52,23 +50,26 @@ export default class Signup extends Component {
return <Redirect to="sent-email-reg" />; return <Redirect to="sent-email-reg" />;
} }
return ( return (
<React.Fragment> <>
<Button circular style={{ margin: "2em" }} href="/"> <Button circular style={{ margin: '2em' }} href="/">
<Icon name="arrow alternate circle left" /> <Icon name="arrow alternate circle left" />
Go Home{" "} Go Home
{' '}
</Button> </Button>
<Grid <Grid
textAlign="center" textAlign="center"
style={{ height: "70vh" }} style={{ height: '70vh' }}
verticalAlign="middle" verticalAlign="middle"
> >
<Grid.Column style={{ maxWidth: 450 }}> <Grid.Column style={{ maxWidth: 450 }}>
<Header as="h2" color="blue" textAlign="center"> <Header as="h2" color="blue" textAlign="center">
<Image src="img/logo.png" /> Sign-up to SmartHut <Image src="img/logo.png" />
{' '}
Sign-up to SmartHut
</Header> </Header>
<Form <Form
size="large" size="large"
style={{ marginTop: "2em" }} style={{ marginTop: '2em' }}
error={this.state.error.state} error={this.state.error.state}
> >
<Message error> <Message error>
@ -107,10 +108,10 @@ export default class Signup extends Component {
iconPosition="left" iconPosition="left"
placeholder="E-mail" placeholder="E-mail"
onChange={this.onChangeHandler} onChange={this.onChangeHandler}
/*error={{ /* error={{
content: 'Please enter a valid email address', content: 'Please enter a valid email address',
pointing: 'below', pointing: 'below',
}}*/ }} */
required required
/> />
<Form.Input <Form.Input
@ -134,7 +135,7 @@ export default class Signup extends Component {
</Form> </Form>
</Grid.Column> </Grid.Column>
</Grid> </Grid>
</React.Fragment> </>
); );
} }
} }

View file

@ -1,6 +1,6 @@
import _ from "lodash"; import _ from 'lodash';
import React from "react"; import React from 'react';
import HeaderController from "./../components/HeaderController"; import HeaderController from '../components/HeaderController';
export default class TestHeaderController extends React.Component { export default class TestHeaderController extends React.Component {
render() { render() {

View file

@ -1,11 +1,12 @@
import React from "react"; import React from 'react';
import VideoTest from "../components/VideoTest"; import VideoTest from '../components/VideoTest';
export default class TestHeaderController extends React.Component { export default class TestHeaderController extends React.Component {
render() { render() {
return ( return (
<div> <div>
<VideoTest />; <VideoTest />
;
</div> </div>
); );
} }

View file

@ -2,6 +2,11 @@
# yarn lockfile v1 # yarn lockfile v1
"@ant-design/css-animation@^1.7.2":
version "1.7.2"
resolved "https://registry.yarnpkg.com/@ant-design/css-animation/-/css-animation-1.7.2.tgz#4ee5d2ec0fb7cc0a78b44e1c82628bd4621ac7e3"
integrity sha512-bvVOe7A+r7lws58B7r+fgnQDK90cV45AXuvGx6i5CCSX1W/M3AJnHsNggDANBxEtWdNdFWcDd5LorB+RdSIlBw==
"@babel/code-frame@7.8.3", "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3": "@babel/code-frame@7.8.3", "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3":
version "7.8.3" version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e"
@ -1009,7 +1014,7 @@
dependencies: dependencies:
regenerator-runtime "^0.13.4" regenerator-runtime "^0.13.4"
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.4", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.4", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
version "7.9.6" version "7.9.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.6.tgz#a9102eb5cadedf3f31d08a9ecf294af7827ea29f" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.6.tgz#a9102eb5cadedf3f31d08a9ecf294af7827ea29f"
integrity sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ== integrity sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ==
@ -1292,15 +1297,14 @@
chalk "^3.0.0" chalk "^3.0.0"
"@material-ui/core@^4.9.4": "@material-ui/core@^4.9.4":
version "4.9.13" version "4.9.14"
resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-4.9.13.tgz#024962bcdda05139e1bad17a1815bf4088702b15" resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-4.9.14.tgz#4388f82cf94554cd3a935774fc12820f3c607a8a"
integrity sha512-GEXNwUr+laZ0N+F1efmHB64Fyg+uQIRXLqbSejg3ebSXgLYNpIjnMOPRfWdu4rICq0dAIgvvNXGkKDMcf3AMpA== integrity sha512-71oYrOpInx5honJ9GzZlygPjmsFhn7Bui61/SWLJsPTkMnfvuZfU3qVqlEHjXyDsnZ+uKmLAIdsrOYnphJxxXw==
dependencies: dependencies:
"@babel/runtime" "^7.4.4" "@babel/runtime" "^7.4.4"
"@material-ui/react-transition-group" "^4.3.0" "@material-ui/styles" "^4.9.14"
"@material-ui/styles" "^4.9.13" "@material-ui/system" "^4.9.14"
"@material-ui/system" "^4.9.13" "@material-ui/types" "^5.1.0"
"@material-ui/types" "^5.0.1"
"@material-ui/utils" "^4.9.12" "@material-ui/utils" "^4.9.12"
"@types/react-transition-group" "^4.2.0" "@types/react-transition-group" "^4.2.0"
clsx "^1.0.4" clsx "^1.0.4"
@ -1308,7 +1312,7 @@
popper.js "^1.16.1-lts" popper.js "^1.16.1-lts"
prop-types "^15.7.2" prop-types "^15.7.2"
react-is "^16.8.0" react-is "^16.8.0"
react-transition-group "^4.3.0" react-transition-group "^4.4.0"
"@material-ui/icons@^4.9.1": "@material-ui/icons@^4.9.1":
version "4.9.1" version "4.9.1"
@ -1317,24 +1321,14 @@
dependencies: dependencies:
"@babel/runtime" "^7.4.4" "@babel/runtime" "^7.4.4"
"@material-ui/react-transition-group@^4.3.0": "@material-ui/styles@^4.9.14":
version "4.3.0" version "4.9.14"
resolved "https://registry.yarnpkg.com/@material-ui/react-transition-group/-/react-transition-group-4.3.0.tgz#92529142addb5cc179dbf42d246c7e3fe4d6104b" resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.9.14.tgz#0a9e93a2bf24e8daa0811411a6f3dabdafbe9a07"
integrity sha512-CwQ0aXrlUynUTY6sh3UvKuvye1o92en20VGAs6TORnSxUYeRmkX8YeTUN3lAkGiBX1z222FxLFO36WWh6q73rQ== integrity sha512-zecwWKgRU2VzdmutNovPB4s5LKI0TWyZKc/AHfPu9iY8tg4UoLjpa4Rn9roYrRfuTbBZHI6b0BXcQ8zkis0nzQ==
dependencies:
"@babel/runtime" "^7.5.5"
dom-helpers "^5.0.1"
loose-envify "^1.4.0"
prop-types "^15.6.2"
"@material-ui/styles@^4.9.13":
version "4.9.13"
resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.9.13.tgz#08b3976bdd21c38bc076693d95834f97539f3b15"
integrity sha512-lWlXJanBdHQ18jW/yphedRokHcvZD1GdGzUF/wQxKDsHwDDfO45ZkAxuSBI202dG+r1Ph483Z3pFykO2obeSRA==
dependencies: dependencies:
"@babel/runtime" "^7.4.4" "@babel/runtime" "^7.4.4"
"@emotion/hash" "^0.8.0" "@emotion/hash" "^0.8.0"
"@material-ui/types" "^5.0.1" "@material-ui/types" "^5.1.0"
"@material-ui/utils" "^4.9.6" "@material-ui/utils" "^4.9.6"
clsx "^1.0.4" clsx "^1.0.4"
csstype "^2.5.2" csstype "^2.5.2"
@ -1349,19 +1343,20 @@
jss-plugin-vendor-prefixer "^10.0.3" jss-plugin-vendor-prefixer "^10.0.3"
prop-types "^15.7.2" prop-types "^15.7.2"
"@material-ui/system@^4.9.13": "@material-ui/system@^4.9.14":
version "4.9.13" version "4.9.14"
resolved "https://registry.yarnpkg.com/@material-ui/system/-/system-4.9.13.tgz#adefb3b6a5ddf0b00fe4e82ac63bb48276e9749d" resolved "https://registry.yarnpkg.com/@material-ui/system/-/system-4.9.14.tgz#4b00c48b569340cefb2036d0596b93ac6c587a5f"
integrity sha512-6AlpvdW6KJJ5bF1Xo2OD13sCN8k+nlL36412/bWnWZOKIfIMo/Lb8c8d1DOIaT/RKWxTEUaWnKZjabVnA3eZjA== integrity sha512-oQbaqfSnNlEkXEziDcJDDIy8pbvwUmZXWNqlmIwDqr/ZdCK8FuV3f4nxikUh7hvClKV2gnQ9djh5CZFTHkZj3w==
dependencies: dependencies:
"@babel/runtime" "^7.4.4" "@babel/runtime" "^7.4.4"
"@material-ui/utils" "^4.9.6" "@material-ui/utils" "^4.9.6"
csstype "^2.5.2"
prop-types "^15.7.2" prop-types "^15.7.2"
"@material-ui/types@^5.0.1": "@material-ui/types@^5.1.0":
version "5.0.1" version "5.1.0"
resolved "https://registry.yarnpkg.com/@material-ui/types/-/types-5.0.1.tgz#c4954063cdc196eb327ee62c041368b1aebb6d61" resolved "https://registry.yarnpkg.com/@material-ui/types/-/types-5.1.0.tgz#efa1c7a0b0eaa4c7c87ac0390445f0f88b0d88f2"
integrity sha512-wURPSY7/3+MAtng3i26g+WKwwNE3HEeqa/trDBR5+zWKmcjO+u9t7Npu/J1r+3dmIa/OeziN9D/18IrBKvKffw== integrity sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==
"@material-ui/utils@^4.9.12", "@material-ui/utils@^4.9.6": "@material-ui/utils@^4.9.12", "@material-ui/utils@^4.9.6":
version "4.9.12" version "4.9.12"
@ -1623,9 +1618,9 @@
"@types/node" "*" "@types/node" "*"
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
version "2.0.1" version "2.0.2"
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.2.tgz#79d7a78bad4219f4c03d6557a1c72d9ca6ba62d5"
integrity sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg== integrity sha512-rsZg7eL+Xcxsxk2XlBt9KcG8nOp9iYdKCOikY9x2RFJCyOdNj4MKPQty0e8oZr29vVAzKXr1BmR+kZauti3o1w==
"@types/istanbul-lib-report@*": "@types/istanbul-lib-report@*":
version "3.0.0" version "3.0.0"
@ -1635,9 +1630,9 @@
"@types/istanbul-lib-coverage" "*" "@types/istanbul-lib-coverage" "*"
"@types/istanbul-reports@^1.1.1": "@types/istanbul-reports@^1.1.1":
version "1.1.1" version "1.1.2"
resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz#7a8cbf6a406f36c8add871625b278eaf0b0d255a" resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz#e875cc689e47bce549ec81f3df5e6f6f11cfaeb2"
integrity sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA== integrity sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==
dependencies: dependencies:
"@types/istanbul-lib-coverage" "*" "@types/istanbul-lib-coverage" "*"
"@types/istanbul-lib-report" "*" "@types/istanbul-lib-report" "*"
@ -1653,9 +1648,9 @@
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
"@types/node@*": "@types/node@*":
version "13.13.5" version "14.0.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.5.tgz#96ec3b0afafd64a4ccea9107b75bf8489f0e5765" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.1.tgz#5d93e0a099cd0acd5ef3d5bde3c086e1f49ff68c"
integrity sha512-3ySmiBYJPqgjiHA7oEaIo2Rzz0HrOZ7yrNO5HWyaE5q0lQ3BppDZ3N53Miz8bw2I7gh1/zir2MGVZBvpb1zq9g== integrity sha512-FAYBGwC+W6F9+huFIDtn43cpy7+SzG+atzRiTfdp3inUKL2hXnd4rG8hylJLIh4+hqrQy1P17kvJByE/z825hA==
"@types/parse-json@^4.0.0": "@types/parse-json@^4.0.0":
version "4.0.0" version "4.0.0"
@ -1673,9 +1668,9 @@
integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw== integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==
"@types/react-dom@*": "@types/react-dom@*":
version "16.9.7" version "16.9.8"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.7.tgz#60844d48ce252d7b2dccf0c7bb937130e27c0cd2" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.8.tgz#fe4c1e11dfc67155733dfa6aa65108b4971cb423"
integrity sha512-GHTYhM8/OwUCf254WO5xqR/aqD3gC9kSTLpopWGpQLpnw23jk44RvMHsyUSEplvRJZdHxhJGMMLF0kCPYHPhQA== integrity sha512-ykkPQ+5nFknnlU6lDd947WbQ6TE3NNzbQAkInC2EKY1qeYdTKp7onFusmYZb+ityzx2YviqT6BXSu+LyWWJwcA==
dependencies: dependencies:
"@types/react" "*" "@types/react" "*"
@ -1687,9 +1682,9 @@
"@types/react" "*" "@types/react" "*"
"@types/react@*": "@types/react@*":
version "16.9.34" version "16.9.35"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.34.tgz#f7d5e331c468f53affed17a8a4d488cd44ea9349" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.35.tgz#a0830d172e8aadd9bd41709ba2281a3124bbd368"
integrity sha512-8AJlYMOfPe1KGLKyHpflCg5z46n0b5DbRfqDksxBLBTUpB75ypDBAO9eCUcjNwE6LCUslwTz00yyG/X9gaVtow== integrity sha512-q0n0SsWcGc8nDqH2GJfWQWUOmZSJhXV64CjVN5SvcNti3TdEaA3AH0D8DwNmMdzjMAC/78tB8nAZIlV8yTz+zQ==
dependencies: dependencies:
"@types/prop-types" "*" "@types/prop-types" "*"
csstype "^2.2.0" csstype "^2.2.0"
@ -1728,60 +1723,60 @@
integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw== integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==
"@types/yargs@^13.0.0": "@types/yargs@^13.0.0":
version "13.0.8" version "13.0.9"
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.8.tgz#a38c22def2f1c2068f8971acb3ea734eb3c64a99" resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.9.tgz#44028e974343c7afcf3960f1a2b1099c39a7b5e1"
integrity sha512-XAvHLwG7UQ+8M4caKIH0ZozIOYay5fQkAgyIXegXT9jPtdIGdhga+sUEdAr1CiG46aB+c64xQEYyEzlwWVTNzA== integrity sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==
dependencies: dependencies:
"@types/yargs-parser" "*" "@types/yargs-parser" "*"
"@types/yargs@^15.0.0": "@types/yargs@^15.0.0":
version "15.0.4" version "15.0.5"
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.4.tgz#7e5d0f8ca25e9d5849f2ea443cf7c402decd8299" resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.5.tgz#947e9a6561483bdee9adffc983e91a6902af8b79"
integrity sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg== integrity sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==
dependencies: dependencies:
"@types/yargs-parser" "*" "@types/yargs-parser" "*"
"@typescript-eslint/eslint-plugin@^2.10.0": "@typescript-eslint/eslint-plugin@^2.10.0":
version "2.31.0" version "2.33.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.31.0.tgz#942c921fec5e200b79593c71fafb1e3f57aa2e36" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.33.0.tgz#d6c8319d5011b4783bb3d2dadf105d8bdd499bd5"
integrity sha512-iIC0Pb8qDaoit+m80Ln/aaeu9zKQdOLF4SHcGLarSeY1gurW6aU4JsOPMjKQwXlw70MvWKZQc6S2NamA8SJ/gg== integrity sha512-QV6P32Btu1sCI/kTqjTNI/8OpCYyvlGjW5vD8MpTIg+HGE5S88HtT1G+880M4bXlvXj/NjsJJG0aGcVh0DdbeQ==
dependencies: dependencies:
"@typescript-eslint/experimental-utils" "2.31.0" "@typescript-eslint/experimental-utils" "2.33.0"
functional-red-black-tree "^1.0.1" functional-red-black-tree "^1.0.1"
regexpp "^3.0.0" regexpp "^3.0.0"
tsutils "^3.17.1" tsutils "^3.17.1"
"@typescript-eslint/experimental-utils@2.31.0": "@typescript-eslint/experimental-utils@2.33.0":
version "2.31.0" version "2.33.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.31.0.tgz#a9ec514bf7fd5e5e82bc10dcb6a86d58baae9508" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.33.0.tgz#000f1e5f344fbea1323dc91cc174805d75f99a03"
integrity sha512-MI6IWkutLYQYTQgZ48IVnRXmLR/0Q6oAyJgiOror74arUMh7EWjJkADfirZhRsUMHeLJ85U2iySDwHTSnNi9vA== integrity sha512-qzPM2AuxtMrRq78LwyZa8Qn6gcY8obkIrBs1ehqmQADwkYzTE1Pb4y2W+U3rE/iFkSWcWHG2LS6MJfj6SmHApg==
dependencies: dependencies:
"@types/json-schema" "^7.0.3" "@types/json-schema" "^7.0.3"
"@typescript-eslint/typescript-estree" "2.31.0" "@typescript-eslint/typescript-estree" "2.33.0"
eslint-scope "^5.0.0" eslint-scope "^5.0.0"
eslint-utils "^2.0.0" eslint-utils "^2.0.0"
"@typescript-eslint/parser@^2.10.0": "@typescript-eslint/parser@^2.10.0":
version "2.31.0" version "2.33.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.31.0.tgz#beddd4e8efe64995108b229b2862cd5752d40d6f" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.33.0.tgz#395c0ef229ebef883608f8632a34f0acf02b9bdd"
integrity sha512-uph+w6xUOlyV2DLSC6o+fBDzZ5i7+3/TxAsH4h3eC64tlga57oMb96vVlXoMwjR/nN+xyWlsnxtbDkB46M2EPQ== integrity sha512-AUtmwUUhJoH6yrtxZMHbRUEMsC2G6z5NSxg9KsROOGqNXasM71I8P2NihtumlWTUCRld70vqIZ6Pm4E5PAziEA==
dependencies: dependencies:
"@types/eslint-visitor-keys" "^1.0.0" "@types/eslint-visitor-keys" "^1.0.0"
"@typescript-eslint/experimental-utils" "2.31.0" "@typescript-eslint/experimental-utils" "2.33.0"
"@typescript-eslint/typescript-estree" "2.31.0" "@typescript-eslint/typescript-estree" "2.33.0"
eslint-visitor-keys "^1.1.0" eslint-visitor-keys "^1.1.0"
"@typescript-eslint/typescript-estree@2.31.0": "@typescript-eslint/typescript-estree@2.33.0":
version "2.31.0" version "2.33.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.31.0.tgz#ac536c2d46672aa1f27ba0ec2140d53670635cfd" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.33.0.tgz#33504c050ccafd38f397a645d4e9534d2eccbb5c"
integrity sha512-vxW149bXFXXuBrAak0eKHOzbcu9cvi6iNcJDzEtOkRwGHxJG15chiAQAwhLOsk+86p9GTr/TziYvw+H9kMaIgA== integrity sha512-d8rY6/yUxb0+mEwTShCQF2zYQdLlqihukNfG9IUlLYz5y1CH6G/9XYbrxQLq3Z14RNvkCC6oe+OcFlyUpwUbkg==
dependencies: dependencies:
debug "^4.1.1" debug "^4.1.1"
eslint-visitor-keys "^1.1.0" eslint-visitor-keys "^1.1.0"
glob "^7.1.6" glob "^7.1.6"
is-glob "^4.0.1" is-glob "^4.0.1"
lodash "^4.17.15" lodash "^4.17.15"
semver "^6.3.0" semver "^7.3.2"
tsutils "^3.17.1" tsutils "^3.17.1"
"@webassemblyjs/ast@1.8.5": "@webassemblyjs/ast@1.8.5":
@ -1982,9 +1977,16 @@ acorn@^6.0.1, acorn@^6.0.4, acorn@^6.2.1:
integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==
acorn@^7.1.1: acorn@^7.1.1:
version "7.1.1" version "7.2.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.2.0.tgz#17ea7e40d7c8640ff54a694c889c26f31704effe"
integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== integrity sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==
add-dom-event-listener@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/add-dom-event-listener/-/add-dom-event-listener-1.1.0.tgz#6a92db3a0dd0abc254e095c0f1dc14acbbaae310"
integrity sha512-WCxx1ixHT0GQU9hb0KI/mhgRQhnU+U3GvwY6ZvVjYq8rsihIGoaIOUbY0yMPBxLH5MDtr0kz3fisWGNcbWW7Jw==
dependencies:
object-assign "4.x"
address@1.1.2, address@^1.0.1: address@1.1.2, address@^1.0.1:
version "1.1.2" version "1.1.2"
@ -2503,7 +2505,7 @@ babel-preset-react-app@^9.1.1:
babel-plugin-macros "2.8.0" babel-plugin-macros "2.8.0"
babel-plugin-transform-react-remove-prop-types "0.4.24" babel-plugin-transform-react-remove-prop-types "0.4.24"
babel-runtime@^6.26.0: babel-runtime@6.x, babel-runtime@^6.26.0:
version "6.26.0" version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4=
@ -2970,9 +2972,9 @@ caniuse-api@^3.0.0:
lodash.uniq "^4.5.0" lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001039, caniuse-lite@^1.0.30001043: caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001039, caniuse-lite@^1.0.30001043:
version "1.0.30001054" version "1.0.30001059"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001054.tgz#7e82fc42d927980b0ce1426c4813df12381e1a75" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001059.tgz#7bff0613d94b6ea41cb5c864c966d340f8ae6d34"
integrity sha512-jiKlTI6Ur8Kjfj8z0muGrV6FscpRvefcQVPSuMuXnvRCfExU7zlVLNjmOz1TnurWgUrAY7MMmjyy+uTgIl1XHw== integrity sha512-oOrc+jPJWooKIA0IrNZ5sYlsXc7NP7KLhNWrSGEJhnfSzDvDJ0zd3i6HXsslExY9bbu+x0FQ5C61LcqmPt7bOQ==
capture-exit@^2.0.0: capture-exit@^2.0.0:
version "2.0.0" version "2.0.0"
@ -3103,7 +3105,7 @@ class-utils@^0.3.5:
isobject "^3.0.0" isobject "^3.0.0"
static-extend "^0.1.1" static-extend "^0.1.1"
classnames@^2.2.3, classnames@^2.2.6: classnames@2.x, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6:
version "2.2.6" version "2.2.6"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==
@ -4106,6 +4108,11 @@ dom-accessibility-api@^0.3.0:
resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.3.0.tgz#511e5993dd673b97c87ea47dba0e3892f7e0c983" resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.3.0.tgz#511e5993dd673b97c87ea47dba0e3892f7e0c983"
integrity sha512-PzwHEmsRP3IGY4gv/Ug+rMeaTIyTJvadCb+ujYXYeIylbHJezIyNToe8KfEgHTCEYyC+/bUghYOGg8yMGlZ6vA== integrity sha512-PzwHEmsRP3IGY4gv/Ug+rMeaTIyTJvadCb+ujYXYeIylbHJezIyNToe8KfEgHTCEYyC+/bUghYOGg8yMGlZ6vA==
dom-align@^1.7.0:
version "1.12.0"
resolved "https://registry.yarnpkg.com/dom-align/-/dom-align-1.12.0.tgz#56fb7156df0b91099830364d2d48f88963f5a29c"
integrity sha512-YkoezQuhp3SLFGdOlr5xkqZ640iXrnHAwVYcDg8ZKRUtO7mSzSC2BA5V0VuyAwPSJA4CLIc6EDDJh4bEsD2+zA==
dom-converter@^0.2: dom-converter@^0.2:
version "0.2.0" version "0.2.0"
resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768"
@ -4251,9 +4258,9 @@ ee-first@1.1.1:
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
electron-to-chromium@^1.3.378, electron-to-chromium@^1.3.413: electron-to-chromium@^1.3.378, electron-to-chromium@^1.3.413:
version "1.3.432" version "1.3.438"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.432.tgz#3bf7b191978ff2e8bc3caf811bb52b1e9f9eab25" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.438.tgz#56051a9b148842fec813b113e8070ae892a85920"
integrity sha512-/GdNhXyLP5Yl2322CUX/+Xi8NhdHBqL6lD9VJVKjH6CjoPGakvwZ5CpKgj/oOlbzuWWjOvMjDw1bBuAIRCNTlw== integrity sha512-QKMcpfA/fCOnqFHsZvKr2haQQb3eXkDI17zT+4hHxJJThyN5nShcG6q1VR8vRiE/2GCJM+0p3PzinYknkdsBYg==
elliptic@^6.0.0, elliptic@^6.5.2: elliptic@^6.0.0, elliptic@^6.5.2:
version "6.5.2" version "6.5.2"
@ -4322,9 +4329,9 @@ entities@^1.1.1:
integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
entities@^2.0.0: entities@^2.0.0:
version "2.0.0" version "2.0.2"
resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.2.tgz#ac74db0bba8d33808bbf36809c3a5c3683531436"
integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== integrity sha512-dmD3AvJQBUjKpcNkoqr+x+IF0SdRtPz9Vk0uTy4yWqga9ibB6s4v++QFWNohjiUGoMlF552ZvNyXDxz5iW0qmw==
errno@^0.1.3, errno@~0.1.7: errno@^0.1.3, errno@~0.1.7:
version "0.1.7" version "0.1.7"
@ -4419,6 +4426,24 @@ escodegen@^1.11.0, escodegen@^1.9.1:
optionalDependencies: optionalDependencies:
source-map "~0.6.1" source-map "~0.6.1"
eslint-config-airbnb-base@^14.1.0:
version "14.1.0"
resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.1.0.tgz#2ba4592dd6843258221d9bff2b6831bd77c874e4"
integrity sha512-+XCcfGyCnbzOnktDVhwsCAx+9DmrzEmuwxyHUJpw+kqBVT744OUBrB09khgFKlK1lshVww6qXGsYPZpavoNjJw==
dependencies:
confusing-browser-globals "^1.0.9"
object.assign "^4.1.0"
object.entries "^1.1.1"
eslint-config-airbnb@^18.1.0:
version "18.1.0"
resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-18.1.0.tgz#724d7e93dadd2169492ff5363c5aaa779e01257d"
integrity sha512-kZFuQC/MPnH7KJp6v95xsLBf63G/w7YqdPfQ0MUanxQ7zcKUNG8j+sSY860g3NwCBOa62apw16J6pRN+AOgXzw==
dependencies:
eslint-config-airbnb-base "^14.1.0"
object.assign "^4.1.0"
object.entries "^1.1.1"
eslint-config-react-app@^5.2.0: eslint-config-react-app@^5.2.0:
version "5.2.1" version "5.2.1"
resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-5.2.1.tgz#698bf7aeee27f0cea0139eaef261c7bf7dd623df" resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-5.2.1.tgz#698bf7aeee27f0cea0139eaef261c7bf7dd623df"
@ -4453,6 +4478,11 @@ eslint-module-utils@^2.4.1:
debug "^2.6.9" debug "^2.6.9"
pkg-dir "^2.0.0" pkg-dir "^2.0.0"
eslint-plugin-chai-friendly@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-0.6.0.tgz#54052fab79302ed0cea76ab997351ea4809bfb77"
integrity sha512-Uvvv1gkbRGp/qfN15B0kQyQWg+oFA8buDSqrwmW3egNSk/FpqH2MjQqKOuKwmEL6w4QIQrIjDp+gg6kGGmD3oQ==
eslint-plugin-flowtype@4.6.0: eslint-plugin-flowtype@4.6.0:
version "4.6.0" version "4.6.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-4.6.0.tgz#82b2bd6f21770e0e5deede0228e456cb35308451" resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-4.6.0.tgz#82b2bd6f21770e0e5deede0228e456cb35308451"
@ -4640,9 +4670,9 @@ etag@~1.8.1:
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
eventemitter3@^4.0.0: eventemitter3@^4.0.0:
version "4.0.0" version "4.0.4"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384"
integrity sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg== integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==
events@^3.0.0: events@^3.0.0:
version "3.1.0" version "3.1.0"
@ -5654,9 +5684,9 @@ html-escaper@^2.0.0:
integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
html-minifier-terser@^5.0.1: html-minifier-terser@^5.0.1:
version "5.1.0" version "5.1.1"
resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-5.1.0.tgz#95d3df037f04835e9d1a09d1767c0e361a7de916" resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz#922e96f1f3bb60832c2634b79884096389b1f054"
integrity sha512-tiYE76O1zunboByeB/nFGwUEb263Z3nkNv6Lz2oLC1s6M36bLKfTrjQ+7ssVfaucVllE+N7hh/FbpbxvnIA+LQ== integrity sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==
dependencies: dependencies:
camel-case "^4.1.1" camel-case "^4.1.1"
clean-css "^4.2.3" clean-css "^4.2.3"
@ -7520,14 +7550,13 @@ min-indent@^1.0.0:
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.0.tgz#cfc45c37e9ec0d8f0a0ec3dd4ef7f7c3abe39256" resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.0.tgz#cfc45c37e9ec0d8f0a0ec3dd4ef7f7c3abe39256"
integrity sha1-z8RcN+nsDY8KDsPdTvf3w6vjklY= integrity sha1-z8RcN+nsDY8KDsPdTvf3w6vjklY=
mini-create-react-context@^0.3.0: mini-create-react-context@^0.4.0:
version "0.3.2" version "0.4.0"
resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.3.2.tgz#79fc598f283dd623da8e088b05db8cddab250189" resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.0.tgz#df60501c83151db69e28eac0ef08b4002efab040"
integrity sha512-2v+OeetEyliMt5VHMXsBhABoJ0/M4RCe7fatd/fBy6SMiKazUSEt3gxxypfnk2SHMkdBYvorHRoQxuGoiwbzAw== integrity sha512-b0TytUgFSbgFJGzJqXPKCFCBWigAjpjo+Fl7Vf7ZbKRDptszpppKxXH6DRXEABZ/gcEQczeb0iZ7JvL8e8jjCA==
dependencies: dependencies:
"@babel/runtime" "^7.4.0" "@babel/runtime" "^7.5.5"
gud "^1.0.0" tiny-warning "^1.0.3"
tiny-warning "^1.0.2"
mini-css-extract-plugin@0.9.0: mini-css-extract-plugin@0.9.0:
version "0.9.0" version "0.9.0"
@ -7576,16 +7605,16 @@ minipass-flush@^1.0.5:
minipass "^3.0.0" minipass "^3.0.0"
minipass-pipeline@^1.2.2: minipass-pipeline@^1.2.2:
version "1.2.2" version "1.2.3"
resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.2.tgz#3dcb6bb4a546e32969c7ad710f2c79a86abba93a" resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.3.tgz#55f7839307d74859d6e8ada9c3ebe72cec216a34"
integrity sha512-3JS5A2DKhD2g0Gg8x3yamO0pj7YeKGwVlDS90pF++kxptwx/F+B//roxf9SqYil5tQo65bijy+dAuAFZmYOouA== integrity sha512-cFOknTvng5vqnwOpDsZTWhNll6Jf8o2x+/diplafmxpuIymAjzoOolZG0VvQf3V2HgqzJNhnuKHYp2BqDgz8IQ==
dependencies: dependencies:
minipass "^3.0.0" minipass "^3.0.0"
minipass@^3.0.0, minipass@^3.1.1: minipass@^3.0.0, minipass@^3.1.1:
version "3.1.1" version "3.1.3"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.1.tgz#7607ce778472a185ad6d89082aa2070f79cedcd5" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd"
integrity sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w== integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==
dependencies: dependencies:
yallist "^4.0.0" yallist "^4.0.0"
@ -7894,7 +7923,7 @@ oauth-sign@~0.9.0:
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: object-assign@4.x, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1" version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
@ -9173,9 +9202,9 @@ postcss@7.0.21:
supports-color "^6.1.0" supports-color "^6.1.0"
postcss@^7, postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.23, postcss@^7.0.27, postcss@^7.0.5, postcss@^7.0.6: postcss@^7, postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.23, postcss@^7.0.27, postcss@^7.0.5, postcss@^7.0.6:
version "7.0.29" version "7.0.30"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.29.tgz#d3a903872bd52280b83bce38cdc83ce55c06129e" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.30.tgz#cc9378beffe46a02cbc4506a0477d05fcea9a8e2"
integrity sha512-ba0ApvR3LxGvRMMiUa9n0WR4HjzcYm7tS+ht4/2Nd0NLtHpPIH77fuB9Xh1/yJVz9O/E/95Y/dn8ygWsyffXtw== integrity sha512-nu/0m+NtIzoubO+xdAlwZl/u5S5vi/y6BCsoL8D+8IxsD3XvBS8X4YEADNIVXKVuQvduiucnRv+vPIqj56EGMQ==
dependencies: dependencies:
chalk "^2.4.2" chalk "^2.4.2"
source-map "^0.6.1" source-map "^0.6.1"
@ -9420,7 +9449,7 @@ querystringify@^2.1.1:
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e"
integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==
raf@^3.4.1: raf@^3.4.0, raf@^3.4.1:
version "3.4.1" version "3.4.1"
resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39"
integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==
@ -9457,6 +9486,67 @@ raw-body@2.4.0:
iconv-lite "0.4.24" iconv-lite "0.4.24"
unpipe "1.0.0" unpipe "1.0.0"
rc-align@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/rc-align/-/rc-align-3.0.0.tgz#5b0510aaa12dfef7b8d79877d39e43de5ef831bc"
integrity sha512-/T/4LOlKJLFe8EwsORuc3pFWOJ8caUpj2vtKIHWea4PhakoleM7KDQsx0n1WDQENIeSfrP9P1FowVxAdvhjsvw==
dependencies:
classnames "2.x"
dom-align "^1.7.0"
rc-util "^4.12.0"
resize-observer-polyfill "^1.5.1"
rc-animate@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/rc-animate/-/rc-animate-3.0.0.tgz#f76d7051136b7d650fb3d29366a160e95a369c0a"
integrity sha512-+ANeyCei4lWSJHWTcocywdYAy6lpRdBva/7Fs3nBBiAngW/W+Gmx+gQEcsmcgQBqziWUYnR91Bk12ltR3GBHPA==
dependencies:
"@ant-design/css-animation" "^1.7.2"
classnames "^2.2.6"
raf "^3.4.0"
rc-util "^4.15.3"
rc-slider@^9.2.4:
version "9.2.4"
resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-9.2.4.tgz#92e2b58c53def9921ae0fc2822727ab5785b9ed0"
integrity sha512-wSr7vz+WtzzGqsGU2rTQ4mmLz9fkuIDMPYMYm8ygYFvxQ2Rh4uRhOWHYI0R8krNK5k1bGycckYxmQqUIvLAh3w==
dependencies:
babel-runtime "6.x"
classnames "^2.2.5"
rc-tooltip "^4.0.0"
rc-util "^4.0.4"
shallowequal "^1.1.0"
warning "^4.0.3"
rc-tooltip@^4.0.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-4.2.0.tgz#614235c94874861b7a473793992956fb666e2ba2"
integrity sha512-Ehf7nTQkn+2Y5J1awYIH8RmMTzHEBj+V7VpLLgCjMVMbigJLdW6pcB2XK6oSGUKl9VGzONdWqya+OKHIPd3D1Q==
dependencies:
rc-trigger "^4.2.1"
rc-trigger@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/rc-trigger/-/rc-trigger-4.2.1.tgz#e30271c703d3a470fd47e75b7432102c7d0c9449"
integrity sha512-iFQ+/FbzDvYDrTS3jXbdk4MgVNU0R/A8UAAQkspXSr4Q6jTcR6p+lfNhSS0JJgJuXtfjoInC0+8jXK8HUShQ0g==
dependencies:
classnames "^2.2.6"
raf "^3.4.1"
rc-align "^3.0.0"
rc-animate "^3.0.0"
rc-util "^4.20.0"
rc-util@^4.0.4, rc-util@^4.12.0, rc-util@^4.15.3, rc-util@^4.20.0:
version "4.20.5"
resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-4.20.5.tgz#f7c77569e971ae6a8ad56f899cadd22275398325"
integrity sha512-f67s4Dt1quBYhrVPq5QMKmK3eS2hN1NNIAyhaiG0HmvqiGYAXMQ7SP2AlGqv750vnzhJs38JklbkWT1/wjhFPg==
dependencies:
add-dom-event-listener "^1.1.0"
prop-types "^15.5.10"
react-is "^16.12.0"
react-lifecycles-compat "^3.0.4"
shallowequal "^1.1.0"
react-app-polyfill@^1.0.6: react-app-polyfill@^1.0.6:
version "1.0.6" version "1.0.6"
resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-1.0.6.tgz#890f8d7f2842ce6073f030b117de9130a5f385f0" resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-1.0.6.tgz#890f8d7f2842ce6073f030b117de9130a5f385f0"
@ -9546,6 +9636,11 @@ react-is@^16.12.0, react-is@^16.6.0, react-is@^16.6.3, react-is@^16.7.0, react-i
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-lifecycles-compat@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
react-modal@^2.2.2: react-modal@^2.2.2:
version "2.4.1" version "2.4.1"
resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-2.4.1.tgz#cb09b26711b148eb9f59cb180e1b7d82980ded05" resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-2.4.1.tgz#cb09b26711b148eb9f59cb180e1b7d82980ded05"
@ -9595,28 +9690,28 @@ react-round-slider@^1.0.1:
react "16.4.2" react "16.4.2"
react-router-dom@^5.1.2: react-router-dom@^5.1.2:
version "5.1.2" version "5.2.0"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.1.2.tgz#06701b834352f44d37fbb6311f870f84c76b9c18" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662"
integrity sha512-7BPHAaIwWpZS074UKaw1FjVdZBSVWEk8IuDXdB+OkLb8vd/WRQIpA4ag9WQk61aEfQs47wHyjWUoUGGZxpQXew== integrity sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==
dependencies: dependencies:
"@babel/runtime" "^7.1.2" "@babel/runtime" "^7.1.2"
history "^4.9.0" history "^4.9.0"
loose-envify "^1.3.1" loose-envify "^1.3.1"
prop-types "^15.6.2" prop-types "^15.6.2"
react-router "5.1.2" react-router "5.2.0"
tiny-invariant "^1.0.2" tiny-invariant "^1.0.2"
tiny-warning "^1.0.0" tiny-warning "^1.0.0"
react-router@5.1.2, react-router@^5.1.2: react-router@5.2.0, react-router@^5.1.2:
version "5.1.2" version "5.2.0"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.1.2.tgz#6ea51d789cb36a6be1ba5f7c0d48dd9e817d3418" resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.2.0.tgz#424e75641ca8747fbf76e5ecca69781aa37ea293"
integrity sha512-yjEuMFy1ONK246B+rsa0cUam5OeAQ8pyclRDgpxuSCrAlJ1qN9uZ5IgyKC7gQg0w8OM50NXHEegPh/ks9YuR2A== integrity sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==
dependencies: dependencies:
"@babel/runtime" "^7.1.2" "@babel/runtime" "^7.1.2"
history "^4.9.0" history "^4.9.0"
hoist-non-react-statics "^3.1.0" hoist-non-react-statics "^3.1.0"
loose-envify "^1.3.1" loose-envify "^1.3.1"
mini-create-react-context "^0.3.0" mini-create-react-context "^0.4.0"
path-to-regexp "^1.7.0" path-to-regexp "^1.7.0"
prop-types "^15.6.2" prop-types "^15.6.2"
react-is "^16.6.0" react-is "^16.6.0"
@ -9683,7 +9778,7 @@ react-scripts@3.4.0:
optionalDependencies: optionalDependencies:
fsevents "2.1.2" fsevents "2.1.2"
react-transition-group@^4.3.0: react-transition-group@^4.4.0:
version "4.4.1" version "4.4.1"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9"
integrity sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw== integrity sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==
@ -9997,7 +10092,7 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
resize-observer-polyfill@^1.4.2: resize-observer-polyfill@^1.4.2, resize-observer-polyfill@^1.5.1:
version "1.5.1" version "1.5.1"
resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
@ -10164,9 +10259,9 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0:
version "5.2.0" version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
safe-regex@^1.1.0: safe-regex@^1.1.0:
version "1.1.0" version "1.1.0"
@ -10299,6 +10394,11 @@ semver@7.0.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
semver@^7.3.2:
version "7.3.2"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938"
integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==
send@0.17.1: send@0.17.1:
version "0.17.1" version "0.17.1"
resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
@ -10625,9 +10725,9 @@ spdx-exceptions@^2.1.0:
integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==
spdx-expression-parse@^3.0.0: spdx-expression-parse@^3.0.0:
version "3.0.0" version "3.0.1"
resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679"
integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg== integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==
dependencies: dependencies:
spdx-exceptions "^2.1.0" spdx-exceptions "^2.1.0"
spdx-license-ids "^3.0.0" spdx-license-ids "^3.0.0"
@ -11172,7 +11272,7 @@ tiny-invariant@^1.0.2:
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875"
integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw== integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==
tiny-warning@^1.0.0, tiny-warning@^1.0.2: tiny-warning@^1.0.0, tiny-warning@^1.0.2, tiny-warning@^1.0.3:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
@ -11274,9 +11374,9 @@ ts-pnp@^1.1.2:
integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==
tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0: tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0:
version "1.11.2" version "1.13.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.2.tgz#9c79d83272c9a7aaf166f73915c9667ecdde3cc9" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
integrity sha512-tTSkux6IGPnUGUd1XAZHcpu85MOkIl5zX49pO+jfsie3eP0B6pyhOlLXm3cAC6T7s+euSDDUUV+Acop5WmtkVg== integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==
tsutils@^3.17.1: tsutils@^3.17.1:
version "3.17.1" version "3.17.1"

View file

@ -1,4 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1