Merge branch 'dev' of lab.si.usi.ch:sa4-2020/the-sanmarinoes/frontend into 103-temperature-sensor-color-circle-change-with-temperature-value

This commit is contained in:
Claudio Maggioni 2020-05-08 15:21:49 +02:00
commit 38425b8d68
19 changed files with 252 additions and 11550 deletions

View file

@ -3,6 +3,7 @@ image: node:latest
stages: stages:
- build - build
- test - test
- code_quality
- deploy - deploy
cache: cache:
@ -24,6 +25,14 @@ testing_testing:
- cd smart-hut - cd smart-hut
- yarn test - yarn test
sonar-scanner:
stage: code_quality
only:
- dev
script:
- 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
smartHut_deploy: smartHut_deploy:
stage: deploy stage: deploy
tags: tags:

View file

@ -1075,6 +1075,14 @@
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
}, },
"@giantmachines/redux-websocket": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@giantmachines/redux-websocket/-/redux-websocket-1.2.0.tgz",
"integrity": "sha512-87GXE32CnsA9/AA7jVDQupWLmycHAuoWPwNCTz2YnlRR1l8EqLlOJK6SyeMQaVRIuAvM5B7e06dylmaS9Jlgnw==",
"requires": {
"redux": "~4"
}
},
"@hapi/address": { "@hapi/address": {
"version": "2.1.4", "version": "2.1.4",
"resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz", "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz",
@ -6356,6 +6364,14 @@
"resolved": "https://registry.npmjs.org/immer/-/immer-1.10.0.tgz", "resolved": "https://registry.npmjs.org/immer/-/immer-1.10.0.tgz",
"integrity": "sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg==" "integrity": "sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg=="
}, },
"immutability-helper": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/immutability-helper/-/immutability-helper-3.0.2.tgz",
"integrity": "sha512-fcrJ26wpvUcuGRpoGY4hyQ/JOeR1HAunMmE3C0XYXSe6plAGtgTlB2S4BzueBANCPrDJ7AByL1yrIRLIlVfwpA==",
"requires": {
"invariant": "^2.2.4"
}
},
"import-cwd": { "import-cwd": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz",
@ -10753,6 +10769,11 @@
"resolved": "https://registry.npmjs.org/react-circular-slider-svg/-/react-circular-slider-svg-0.1.5.tgz", "resolved": "https://registry.npmjs.org/react-circular-slider-svg/-/react-circular-slider-svg-0.1.5.tgz",
"integrity": "sha512-ZXSlwKHExjb950+84gydH5wxX1CnND1v4w1DywOHcOgzD48Wmjr+nME4JeOumNJ9QOXhm1HROhSEDMlTFIAGqw==" "integrity": "sha512-ZXSlwKHExjb950+84gydH5wxX1CnND1v4w1DywOHcOgzD48Wmjr+nME4JeOumNJ9QOXhm1HROhSEDMlTFIAGqw=="
}, },
"react-confirm-alert": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/react-confirm-alert/-/react-confirm-alert-2.6.1.tgz",
"integrity": "sha512-KxlpQoR4x/ET1oFPm/IGpsqnpzP17qkkQZuaO3pw7zGZ9oP5hElPtq/1vgoikoqNHQ2tMm6Iw9HQUNLoNgXkRA=="
},
"react-dev-utils": { "react-dev-utils": {
"version": "10.2.0", "version": "10.2.0",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-10.2.0.tgz", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-10.2.0.tgz",
@ -10968,6 +10989,27 @@
"warning": "^4.0.2" "warning": "^4.0.2"
} }
}, },
"react-rangeslider": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/react-rangeslider/-/react-rangeslider-2.2.0.tgz",
"integrity": "sha512-5K7Woa+cyqZ5wiW5+KhqGV+3+FiFxGKQ9rUxTMh52sObXVYEeBbfxFrp1eBvS8mRIxnUbHz9ppnFP0LhwOyNeg==",
"requires": {
"classnames": "^2.2.3",
"resize-observer-polyfill": "^1.4.2"
}
},
"react-redux": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.0.tgz",
"integrity": "sha512-EvCAZYGfOLqwV7gh849xy9/pt55rJXPwmYvI4lilPM5rUT/1NxuuN59ipdBksRVSvz0KInbPnp4IfoXJXCqiDA==",
"requires": {
"@babel/runtime": "^7.5.5",
"hoist-non-react-statics": "^3.3.0",
"loose-envify": "^1.4.0",
"prop-types": "^15.7.2",
"react-is": "^16.9.0"
}
},
"react-round-slider": { "react-round-slider": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/react-round-slider/-/react-round-slider-1.0.1.tgz", "resolved": "https://registry.npmjs.org/react-round-slider/-/react-round-slider-1.0.1.tgz",
@ -11163,6 +11205,20 @@
"strip-indent": "^3.0.0" "strip-indent": "^3.0.0"
} }
}, },
"redux": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz",
"integrity": "sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==",
"requires": {
"loose-envify": "^1.4.0",
"symbol-observable": "^1.2.0"
}
},
"redux-thunk": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz",
"integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw=="
},
"regenerate": { "regenerate": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",
@ -11382,6 +11438,11 @@
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
}, },
"resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
},
"resolve": { "resolve": {
"version": "1.15.0", "version": "1.15.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.0.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.0.tgz",
@ -12625,6 +12686,11 @@
"util.promisify": "~1.0.0" "util.promisify": "~1.0.0"
} }
}, },
"symbol-observable": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
"integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
},
"symbol-tree": { "symbol-tree": {
"version": "3.2.4", "version": "3.2.4",
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",

View file

@ -17,6 +17,7 @@
"react-axios": "^2.0.3", "react-axios": "^2.0.3",
"react-circular-input": "^0.1.6", "react-circular-input": "^0.1.6",
"react-circular-slider-svg": "^0.1.5", "react-circular-slider-svg": "^0.1.5",
"react-confirm-alert": "^2.6.1",
"react-device-detect": "^1.11.14", "react-device-detect": "^1.11.14",
"react-dom": "^16.12.0", "react-dom": "^16.12.0",
"react-modal": "^2.2.2", "react-modal": "^2.2.2",
@ -53,6 +54,7 @@
] ]
}, },
"devDependencies": { "devDependencies": {
"prettier": "2.0.1" "prettier": "2.0.1",
"sonarqube-scanner": "^2.6.0"
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View file

@ -8,6 +8,7 @@ import {
Icon, Icon,
Responsive, Responsive,
Image, Image,
Confirm,
} from "semantic-ui-react"; } from "semantic-ui-react";
import SelectIcons from "./SelectIcons"; import SelectIcons from "./SelectIcons";
import { connect } from "react-redux"; import { connect } from "react-redux";
@ -34,6 +35,7 @@ class RoomModal extends Component {
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,
}; };
} }
@ -91,6 +93,14 @@ class RoomModal extends Component {
.catch((err) => console.error("error in deleting room", err)); .catch((err) => console.error("error in deleting room", err));
}; };
setSureTrue = () => {
this.setState({ sure: true });
};
setSureFalse = () => {
this.setState({ sure: false });
};
changeSomething = (event) => { changeSomething = (event) => {
let nam = event.target.name; let nam = event.target.name;
let val = event.target.value; let val = event.target.value;
@ -220,16 +230,23 @@ class RoomModal extends Component {
</div> </div>
{this.type === "modify" ? ( {this.type === "modify" ? (
<Button <div>
icon <Button
labelPosition="left" icon
inverted labelPosition="left"
color="red" inverted
onClick={this.deleteRoom} color="red"
> onClick={this.setSureTrue}
<Icon name="trash alternate" /> >
Delete room <Icon name="trash alternate" />
</Button> Delete Room{" "}
</Button>
<Confirm
open={this.state.sure}
onCancel={this.setSureFalse}
onConfirm={this.deleteRoom}
/>
</div>
) : null} ) : null}
</Modal.Content> </Modal.Content>
<Modal.Actions> <Modal.Actions>

View file

@ -1,7 +1,7 @@
// 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 } from "semantic-ui-react"; import { Segment, Card, Header, Icon } from "semantic-ui-react";
import Device from "./devices/Device"; import Device from "./devices/Device";
import NewDevice from "./devices/NewDevice"; import NewDevice from "./devices/NewDevice";
import { connect } from "react-redux"; import { connect } from "react-redux";
@ -25,16 +25,31 @@ class DevicePanel extends Component {
render() { render() {
return ( return (
<Card.Group centered style={{ paddingTop: "3rem" }}> <Card.Group centered style={{ paddingTop: "3rem" }}>
{this.props.devices.map((e, i) => { {this.props.numbeOfRooms > 0 ? (
return <Device key={i} tab={this.props.tab} id={e.id} />; <React.Fragment>
})} {this.props.devices.map((e, i) => {
{!this.props.isActiveRoomHome ? ( return <Device key={i} tab={this.props.tab} id={e.id} />;
<Card style={{ height: "27em" }}> })}
<Segment basic style={{ width: "100%", height: "100%" }}> {!this.props.isActiveRoomHome ? (
<NewDevice /> <Card style={{ height: "27em" }}>
</Segment> <Segment basic style={{ width: "100%", height: "100%" }}>
</Card> <NewDevice />
) : null} </Segment>
</Card>
) : null}
</React.Fragment>
) : (
<Segment placeholder>
<Header icon>
<Icon
name="exclamation triangle"
style={{ paddingBottom: "1rem" }}
/>
Please create a room on the left, and then add devices to the
same.
</Header>
</Segment>
)}
</Card.Group> </Card.Group>
); );
} }
@ -55,6 +70,9 @@ const mapStateToProps = (state, _) => ({
return state.active.activeRoom === -1; return state.active.activeRoom === -1;
}, },
activeRoom: state.active.activeRoom, activeRoom: state.active.activeRoom,
get numbeOfRooms() {
return Object.keys(state.rooms).length;
},
}); });
const DevicePanelContainer = connect( const DevicePanelContainer = connect(
mapStateToProps, mapStateToProps,

View file

@ -16,15 +16,27 @@ class HostsPanel extends Component {
} }
} }
applyHostScene(id) {
this.props
.sceneApply(id, this.props.activeHost)
.then(() => console.log("SCCUESS"))
.catch((err) => console.error("sceneApply update error", err));
}
render() { render() {
if (this.props.isActiveDefaultHost) { if (this.props.isActiveDefaultHost) {
return ( return (
<Segment placeholder style={{ paddingTop: "3rem" }}> <Card.Group centered style={{ paddingTop: "3rem" }}>
<Header icon> <Segment placeholder>
<Icon name="folder open" /> <Header icon>
Please select a host to visit on the left. <Icon
</Header> name="exclamation triangle"
</Segment> style={{ paddingBottom: "1rem" }}
/>
Please select a host to visit on the left.
</Header>
</Segment>
</Card.Group>
); );
} }
@ -41,9 +53,11 @@ class HostsPanel extends Component {
{scene.name} <Icon name={scene.icon} /> {scene.name} <Icon name={scene.icon} />
</Header> </Header>
</Card.Header> </Card.Header>
<Card.Content extras> <Card.Content extras={true}>
<div className="ui two buttons"> <div className="ui two buttons">
<Button>Apply</Button> <Button onClick={() => this.applyHostScene(scene.id)}>
Apply
</Button>
</div> </div>
</Card.Content> </Card.Content>
</Card> </Card>

View file

@ -44,7 +44,10 @@ class ScenesPanel extends Component {
) : ( ) : (
<Segment placeholder> <Segment placeholder>
<Header icon> <Header icon>
<Icon name="folder open" /> <Icon
name="exclamation triangle"
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>

View file

@ -141,6 +141,15 @@ class NewDevice extends Component {
case "buttonDimmer": case "buttonDimmer":
case "knobDimmer": case "knobDimmer":
outputs = this.state.lightsAttached; outputs = this.state.lightsAttached;
if (
this.state.lightsAttached === undefined ||
this.state.lightsAttached.length === 0
) {
alert(
"No lights attached to this switch! Please, add a light a first."
);
return;
}
break; break;
default: default:
break; break;
@ -160,8 +169,18 @@ class NewDevice extends Component {
render() { render() {
const deviceOptions = [ const deviceOptions = [
//stuff //stuff
{ key: "thermostat", text: "Thermostat", value: "thermostat", image: {} }, {
{ key: "curtains", text: "Curtain", value: "curtains", image: {} }, key: "thermostat",
text: "Thermostat",
value: "thermostat",
image: { avatar: true, src: "/img/thermostat-icon.png" },
},
{
key: "curtains",
text: "Curtain",
value: "curtains",
image: { avatar: true, src: "/img/curtains-icon.png" },
},
//stuff ends //stuff ends
{ {
key: "light", key: "light",

View file

@ -70,12 +70,17 @@ class Videocam extends Component {
/> />
</Grid.Column> </Grid.Column>
</Grid.Row> </Grid.Row>
<Grid.Row>
<Grid.Column>
<Checkbox
checked={this.props.stateOrDevice.on}
toggle
label="Turn on/off"
onChange={(e, val) => this.setOnOff(val.checked)}
/>
</Grid.Column>
</Grid.Row>
</Grid> </Grid>
<Checkbox
checked={this.props.stateOrDevice.on}
toggle
onChange={(e, val) => this.setOnOff(val.checked)}
/>
</div> </div>
); );

View file

@ -549,12 +549,21 @@ export const RemoteService = {
}; };
}, },
sceneApply: (id) => { sceneApply: (id, hostId = null) => {
return (dispatch) => { return (dispatch) => {
let url = `/scene/${id}/apply`; let url = `/scene/${id}/apply`;
if (hostId) {
url = url + "?hostId=" + hostId;
}
return Endpoint.post(url) return Endpoint.post(url)
.then((res) => dispatch(actions.deviceOperationUpdate(res.data))) .then((res) =>
dispatch(
hostId
? actions.hostDevicesUpdate(hostId, res.data, true)
: actions.deviceOperationUpdate(res.data)
)
)
.catch((err) => { .catch((err) => {
console.warn("scene apply error", err); console.warn("scene apply error", err);
throw new RemoteError(["Network error"]); throw new RemoteError(["Network error"]);

View file

@ -49,6 +49,7 @@ function reducer(previousState, action) {
[scene.id]: { [scene.id]: {
name: { $set: scene.name }, name: { $set: scene.name },
icon: { $set: scene.icon }, icon: { $set: scene.icon },
guestAccessEnabled: { $set: scene.guestAccessEnabled },
}, },
}, },
}); });

View file

@ -63,9 +63,10 @@ const actions = {
devices, devices,
partial, partial,
}), }),
hostDevicesUpdate: (hostId, devices) => ({ hostDevicesUpdate: (hostId, devices, partial = false) => ({
type: "HOST_DEVICES_UPDATE", type: "HOST_DEVICES_UPDATE",
hostId, hostId,
partial,
devices, devices,
}), }),
stateDelete: (stateId) => ({ stateDelete: (stateId) => ({

View file

@ -81,6 +81,15 @@ class Dashboard extends Component {
} }
render() { render() {
// needed to correctly assign the background image
//in case a room has one.
let backgroundImageHelper;
if (this.activeTab === "Devices") {
backgroundImageHelper = this.props.allRooms;
} else {
backgroundImageHelper = null;
}
//console.log("helper is",helper)
return ( return (
<div style={{ background: "#1b1c1d" }}> <div style={{ background: "#1b1c1d" }}>
<Responsive minWidth={768}> <Responsive minWidth={768}>
@ -126,7 +135,19 @@ class Dashboard extends Component {
</Grid.Column> </Grid.Column>
)} )}
<Grid.Column width={this.hasNavbar ? 13 : 16}> <Grid.Column width={this.hasNavbar ? 13 : 16}>
<div style={panelStyle}>{this.renderTab(this.activeTab)}</div> <div
style={{
backgroundImage: "url(" + backgroundImageHelper + ")",
backgroundColor: "#fafafa",
height: "85vh",
padding: "0rem 3rem",
color: "#000000",
overflow: "auto",
maxHeight: "75vh",
}}
>
{this.renderTab(this.activeTab)}
</div>
</Grid.Column> </Grid.Column>
</Grid.Row> </Grid.Row>
</Grid> </Grid>
@ -199,6 +220,25 @@ class Dashboard extends Component {
const mapStateToProps = (state, _) => ({ const mapStateToProps = (state, _) => ({
activeTab: state.active.activeTab, activeTab: state.active.activeTab,
get currentRoom() {
return state.active.activeRoom;
},
//this took me way longer to figure out than it should have
get allRooms() {
if (state.active.activeRoom == -1) {
return null;
}
for (let i in state.rooms) {
if (i == state.active.activeRoom) {
//console.log("check",state.rooms[i].image)
if (state.rooms[i].image === undefined) {
return null;
} else {
return state.rooms[i].image;
}
}
}
},
}); });
const setActiveTab = (activeTab) => { const setActiveTab = (activeTab) => {

View file

@ -247,7 +247,7 @@ const Home = () => (
</a> </a>
</List.Item> </List.Item>
<List.Item> <List.Item>
<a href="https://theshell.ch" target="blank"> <a href="https://www.theshell.ch" target="blank">
The Shell The Shell
</a> </a>
</List.Item> </List.Item>

View file

@ -116,7 +116,7 @@ export default class Signup extends Component {
<Form.Input <Form.Input
icon="lock" icon="lock"
iconPosition="left" iconPosition="left"
placeholder="Password (at least 8 characters)" placeholder="Password (at least 6 characters)"
name="password" name="password"
type="password" type="password"
onChange={this.onChangeHandler} onChange={this.onChangeHandler}

File diff suppressed because it is too large Load diff

4
yarn.lock Normal file
View file

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