Merge branch 'dev' into '32-finish-dashboard'

# Conflicts:
#   smart-hut/src/components/dashboard/DevicePanel.js
#   smart-hut/src/components/dashboard/devices/Light.js
#   smart-hut/src/components/dashboard/devices/styleComponents.js
#   smart-hut/src/views/Dashboard.js
This commit is contained in:
Matteo Omenetti 2020-03-18 15:06:27 +01:00
commit e60c2ed45a
36 changed files with 421 additions and 135 deletions

View file

@ -3,6 +3,7 @@ image: node:latest
stages:
- build
- test
- deploy
cache:
paths:
@ -19,6 +20,25 @@ install_dependencies:
testing_testing:
stage: test
script:
script:
- cd smart-hut
- yarn test
- yarn test
smartHut_deploy:
stage: deploy
tags:
- dind
image: docker:latest
services:
- docker:dind
variables:
DOCKER_DRIVER: overlay
before_script:
- docker version
- docker info
- docker login -u smarthutsm -p $CI_DOCKER_PASS #GiovanniRoberto
script:
- "docker build -t smarthutsm/smarthut:${CI_COMMIT_BRANCH} ."
- "docker push smarthutsm/smarthut:${CI_COMMIT_BRANCH}"
after_script:
- docker logout

39
Dockerfile Normal file
View file

@ -0,0 +1,39 @@
#FROM mhart/alpine-node:11 AS pleaseGodWork
#WORKDIR /app
#COPY . /app
#RUN ls
#RUN yarn run build
#
#RUN yarn global add serve
#
#
#CMD ["serve", "-p", "8080", "-s", "."]
# base image
FROM node:9.6.1
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
ENV PATH /usr/src/app/node_modules/.bin:$PATH
COPY smart-hut/package.json /usr/src/app/package.json
RUN npm install --silent
RUN npm install react-scripts@1.1.1 -g --silent
CMD ["npm", "start"]
FROM node:9.6.1 as builder
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
ENV PATH /usr/src/app/node_modules/.bin:$PATH
COPY smart-hut/package.json /usr/src/app/package.json
RUN npm install --silent
RUN npm install react-scripts@1.1.1 -g --silent
COPY smart-hut/. /usr/src/app
RUN npm run build
FROM nginx:1.13.9-alpine
COPY --from=builder /usr/src/app/build /usr/share/nginx/html
EXPOSE 8080
CMD ["nginx", "-g", "daemon off;"]

BIN
smart-hut/public/avatar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 644 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
smart-hut/public/title.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9 KiB

BIN
smart-hut/public/title3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9 KiB

BIN
smart-hut/public/title4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

View file

@ -80,12 +80,12 @@ class App extends Component {
};
logout() {
console.log("logout")
this.setState({
loggedIn : false,
});
localStorage.removeItem("token");
this.props.history.push("/");
};
render() {

View file

@ -59,6 +59,28 @@ export var call = {
return err;
});
},
getAllDevices: function(token) {
if (!token){
token = tkn;
}
return axios.get(config + 'device', { headers: { Authorization : "Bearer " + token } })
.then(res => {
return res;
}).catch(err => {
return err;
});
},
getAllDevicesByRoom: function(id, token) {
if (!token){
token = tkn;
}
return axios.get(config + 'room/' + id + '/devices' , { headers: { Authorization : "Bearer " + token } })
.then(res => {
return res;
}).catch(err => {
return err;
});
},
createRoom: function(data, headers) {
return axios.post(config + 'room', data, { headers: { Authorization : "Bearer " + tkn } })
.then(res => {

View file

@ -29,7 +29,7 @@ const HeaderExampleIconProp = () => (
const GridExampleInverted = () => (
const GridExampleInverted = (props) => (
<Grid columns='equal' divided inverted padded >
<Grid.Row color='black' textAlign='center'>
<Grid.Column width={2}>
@ -57,7 +57,7 @@ const GridExampleInverted = () => (
</Dropdown.Item>
<Dropdown.Item>See profile...</Dropdown.Item>
<Dropdown.Divider />
<Dropdown.Item>Logout</Dropdown.Item>
<Dropdown.Item onClick={props.logout }>Logout</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
</Grid.Column>
@ -67,9 +67,10 @@ const GridExampleInverted = () => (
export default class MyHeader extends React.Component {
render() {
console.log(this.props)
return (
<div>
<GridExampleInverted />
<GridExampleInverted logout={this.props.logout} />
</div>
);
}

View file

@ -59,16 +59,42 @@ const devices = [
];
class Panel extends Component {
export default class DevicePanel extends Component {
constructor(props) {
super(props);
this.state = {
devices: devices,
constructor(props) {
super(props);
this.state = {
editMode : false,
devices : this.props.devices,
};
}
editMode: false,
openSettingsModal: false,
settingsDeviceId: null,
editModeController = (e) => this.setState((prevState) => ({ editMode: !prevState.editMode }));
changeDeviceData = (deviceId, newSettings) => {
console.log(newSettings.name, " <-- new name --> ", deviceId );
this.state.devices.map(device => {
if(device.id === deviceId){
for(let prop in newSettings){
if(device.hasOwnProperty(prop)){
if(prop==="name"){
if(checkMaxLength(newSettings[prop])){
device[prop] = newSettings[prop];
}
else{
alert("Name must be less than " + DEVICE_NAME_MAX_LENGTH + " characters.");
}
}else{
device[prop] = newSettings[prop];
}
}
}
}
return null;
});
this.forceUpdate();
};
}
@ -92,69 +118,31 @@ class Panel extends Component {
device[prop] = newSettings[prop];
}
}
}
}
return null;
});
this.forceUpdate();
};
openModal = (settingsDeviceId) => {
this.setState(prevState => ({
openSettingsModal: !prevState.openSettingsModal,
settingsDeviceId: settingsDeviceId
}));
};
render() {
const edit = {
mode: this.state.editMode,
openModal: this.openModal,
};
return (
<div style={panelStyle}>
<button style={editButtonStyle} onClick={this.editModeController}>Edit</button>
<Grid doubling columns={5} divided="vertically">
{this.state.openSettingsModal ?
<SettingsModal openModal={this.openModal} device={this.state.devices.filter(d => d.id === this.state.settingsDeviceId)[0]}/> : ""}
<Grid.Column>
<SmartPlug onChangeData={this.changeDeviceData} device={devices[4]} edit={edit}/>
</Grid.Column>
<Grid.Column>
<Light onChangeData={this.changeDeviceData} device={devices[0]} edit={edit}/>
</Grid.Column>
<Grid.Column>
<Sensor onChangeData={this.changeDeviceData} device={devices[5]} edit={edit}/>
</Grid.Column>
<Grid.Column>
<Switch onChangeData={this.changeDeviceData} device={devices[6]} edit={edit}/>
</Grid.Column>
<Grid.Column>
<NewDevice devices={devices}/>
</Grid.Column>
</Grid>
</div>
)
}
}
export default class DevicePanel extends Component {
constructor(props) {
super(props);
this.state = {
shownRoom: "All"
render() {
return (
<div style={panelStyle}>
<button style={editButtonStyle} onClick={this.editModeController}>Edit</button>
<Grid doubling columns={this.props.devices.length > 0 ? this.props.devices.length : 1} divided="vertically">
{
this.props.devices ?
this.props.devices.map((e, i) => {
return (
<Grid.Column key={i}>
<DeviceType type={e.kind} onChangeData={this.changeDeviceData} device={e} edit={this.state.editMode}/>
</Grid.Column>
)
})
:
null
}
<Grid.Column>
<NewDevice/>
</Grid.Column>
</Grid>
</div>
)
}
}
render() {
return (
<Panel/>
)
}
}
}

View file

@ -0,0 +1,25 @@
import React, { Component } from 'react';
import Light from "./Light";
import SmartPlug from "./SmartPlug";
import Sensor from "./Sensor";
import DefaultDimmer from "./Dimmer";
import Switcher from "./Switcher";
const DeviceType = (props) => {
switch(props.type) {
case "light":
return <Light onChangeData={props.changeDeviceData} device={props.device} edit={props.edit} />
case "sensor":
return <Sensor onChangeData={props.changeDeviceData} device={props.device} edit={props.edit} />
case "dimmer":
return <DefaultDimmer onChangeData={props.changeDeviceData} device={props.device} edit={props.edit} />
case "smartplug":
return <SmartPlug onChangeData={props.changeDeviceData} device={props.device} edit={props.edit} />
case "switch":
return <Switcher onChangeData={props.changeDeviceData} device={props.device} edit={props.edit} />
}
}
export default DeviceType;

View file

@ -30,7 +30,7 @@ export class StatefulDimmer extends Component{
}
}
export class DefaultDimmer extends Component{
export default class DefaultDimmer extends Component{
// As far as I am concern, even though this dimmer doesn't have state, internally it's needed
constructor(props){
super(props);

View file

@ -7,7 +7,8 @@ export default class ModalWindow extends Component {
constructor(props) {
super(props);
this.state = {
selectedIcon: 'home',
id : "",
selectedIcon: "",
name: "",
img: "",
openModal: false
@ -23,14 +24,26 @@ export default class ModalWindow extends Component {
"name" : this.state.name,
"images" : this.state.img
}
console.log(this.props, data);
this.props.addRoom(data);
this.closeModal();
}
modifyRoomModal = (e) => {
let data = {
"icon" : this.state.selectedIcon,
"name" : this.state.name,
"images" : this.state.img
}
this.props.updateRoom(data);
this.closeModal();
}
deleteRoom = (e) => {
console.log(e);
let data = {
"id" : this.state.id
}
this.props.deleteRoom(data);
this.closeModal();
}
changeSomething = (event) => {
@ -49,7 +62,6 @@ export default class ModalWindow extends Component {
}
updateIcon(e) {
console.log(e);
this.setState({selectedIcon : e})
}
@ -64,10 +76,16 @@ export default class ModalWindow extends Component {
return (
<div>
{this.props.type == "new" ?
<Button icon labelPosition='left' inverted onClick={this.openModal}>
<Icon name='plus' size='small'/>
ADD ROOM
</Button>
:
<Icon name="pencil" size="small" onClick={this.openModal}/>
}
<Modal
onClose={this.closeModal}
open={this.state.openModal}>
@ -76,16 +94,19 @@ export default class ModalWindow extends Component {
<Form>
<p>Insert the name of the room:</p>
<Form.Field>
<Input label='Room name' placeholder='Room Name' name="name" type='text' onChange={this.changeSomething}/>
<Input label='Room name' placeholder='Room Name' name="name" type='text' onChange={this.changeSomething}
value={this.props.type == "new" ? null : this.props.idRoom.name }/>
</Form.Field>
<p>Insert an image of the room:</p>
<Form.Field>
<Input label='Room image' type='file' name="img" onChange={this.changeSomething}/>
<Input label='Room image' type='file' name="img" onChange={this.changeSomething}
value={this.props.type == "new" ? null : this.props.idRoom.images }/>
</Form.Field>
</Form>
<div style={spaceDiv}>
<p>Select an icon:</p>
{this.props.type == "new" ? "home" : this.props.idRoom.icon }
<SelectIcons updateIcon={this.updateIcon} currentIcon={this.state.selectedIcon}/>
</div>
@ -100,9 +121,12 @@ export default class ModalWindow extends Component {
<Button color='red' onClick={this.closeModal}>
<Icon name='remove' /> {this.props.type == "new" ? "Cancel" : "Discard changes" }
</Button>
<Button color='green' onClick={this.addRoomModal}>
<Icon name='checkmark' /> {this.props.type == "new" ? "Add room" : "Save changes" }
<Button color='green' onClick={this.props.type == "new" ? this.addRoomModal : this.modifyRoomModal}>
<Icon name='checkmark' /> {this.props.type == "new" ? "Add room" : "Save changes"}
</Button>
</Modal.Actions>
</Modal>
</div>

View file

@ -4,18 +4,24 @@ import Navbar from './Navbar'
import MyHeader from '../components/HeaderController'
import { call } from '../client_server';
import {Button} from 'semantic-ui-react';
import { Menu } from 'semantic-ui-react'
import { Grid, Image, Icon } from 'semantic-ui-react'
import { Grid } from 'semantic-ui-react'
/*
rooms -> actual rooms
activeItem -> the current room in view
devices -> current device in current room view
id of Home is -1
*/
export default class Dashboard extends Component{
constructor(props) {
super(props);
this.state = {
rooms: [],
activeItem: "Home",
tkn: this.props.tkn
activeItem: -1,
devices: [],
tkn: this.props.tkn,
};
this.addRoom = this.addRoom.bind(this);
@ -25,11 +31,17 @@ export default class Dashboard extends Component{
componentDidMount() {
call.getAllRooms(this.props.tkn)
.then(res => {
res.data.forEach((e) => {
this.setState(state => ({
rooms: state.rooms.concat([e])
}));
});
this.setState({
rooms: res.data
});
}).catch(err => {
console.log(err);
});
call.getAllDevices(this.props.tkn)
.then(res => {
this.setState({
devices: res.data
});
}).catch(err => {
console.log(err);
});
@ -50,11 +62,25 @@ export default class Dashboard extends Component{
};
deleteRoom(id) {
call.deleteRoom(id)
.then(res => {
//remove room in state.rooms
}).catch(err => {
console.log(err);
});
}
handleItemClick(el) {
handleItemClick(id) {
// el -> obj of name and id
//da fare richiesta get della room e settare activeItem
call.getAllDevicesByRoom(id, this.props.tkn)
.then(res => {
this.setState({
devices: res.data
});
}).catch(err => {
console.log(err);
});
}
render () {
@ -65,8 +91,8 @@ export default class Dashboard extends Component{
<Grid.Row color='black'>
{/*TODO change this back
<Grid.Column>
<MyHeader />
</Grid.Column>*/}
<MyHeader logout={this.props.logout} />
</Grid.Column>
</Grid.Row>
<Grid.Row color='black'>
<Grid.Column width={3}>
@ -74,7 +100,7 @@ export default class Dashboard extends Component{
</Grid.Column>
<Grid.Column width={13}>
<DevicePanel />
<DevicePanel devices={this.state.devices} />
</Grid.Column>
</Grid.Row>
</Grid>

View file

@ -0,0 +1,107 @@
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,5 +1,5 @@
import React, {Component} from 'react';
import { Message} from 'semantic-ui-react';
import { Header, Grid, Message, Button} from 'semantic-ui-react';
import {Link } from "react-router-dom";
@ -7,13 +7,29 @@ export default class FourOhFour extends Component {
render() {
return (
<Message>
<Message.Header>404 Page Not Found</Message.Header>
<p>
Hey what are you doing here?
Go back to our homepage <Link to="/"/>
</p>
</Message>
<div style={{height : "110vh", background: '#1b1c1d'}}>
<Grid centered>
<Grid.Row>
<Header as='h1'>404</Header>
</Grid.Row>
<Grid.Row>
<Grid.Column width={10}>
<Message>
<Message.Header>404 Page Not Found</Message.Header>
<p>
Hey what are you doing here?
Looks like you are lost, this room does not exist.
</p>
</Message>
</Grid.Column>
<Grid.Column width={6}>
<Button >
<Link to="/">Go back to our main room</Link>
</Button>
</Grid.Column>
</Grid.Row>
</Grid>
</div>
)
}
}

View file

@ -8,27 +8,24 @@ class Navbar extends Component {
super(props);
this.state = {
activeItem: 'Home',
edited: ""
edited: "",
editMode : false
}
}
showForm = (event) => {
console.log(event);
if (event === "new"){
console.log("funziona");
return (<ModalWindow type="new"/>)
}
}
editModeController = (e) => {
this.setState((prevState) => ({ editMode: !prevState.editMode }));
};
handleClick = (e, { i }) => {
this.setState({ activeItem: e.name,
edited: i
handleClick = (e, {id}) => {
console.log(id);
this.setState({ activeItem: id,
});
this.props.handleItemClick(e)
this.props.handleItemClick(id)
}
render(){
const { activeItem } = this.state
//const { activeItem } = this.state
return (
<div>
<Segment.Group>
@ -40,8 +37,9 @@ class Navbar extends Component {
<Grid.Row color='black'>
<Menu inverted fluid vertical>
<Menu.Item
key={-1}
name='Home'
active={activeItem === 'Home'}
active={this.state.activeItem === 'Home'}
onClick={this.handleClick}
>
<Grid>
@ -50,7 +48,7 @@ class Navbar extends Component {
<Icon name="home" size="small"/>
</Grid.Column>
<Grid.Column>
Home
HOME
</Grid.Column>
</Grid.Row>
</Grid>
@ -64,38 +62,38 @@ class Navbar extends Component {
id={e.id}
key={i}
name={e.name}
active={activeItem === e.name}
active={this.state.activeItem === e.id}
onClick={this.handleClick}
>
<Grid>
<Grid.Row>
<Grid.Column width={1}>
<Grid.Column>
<Icon name={e.icon} size="small"/>
</Grid.Column>
<Grid.Column width={12}>
<Grid.Column width={8}>
{e.name}
</Grid.Column>
<Grid.Column width={1}>
<Icon name="pencil" size="small"/>
<Grid.Column floated="right">
{this.state.edit ?
<ModalWindow type="modify" idRoom={e} modifyRoom={this.props.updateRoom} deleteRoom={this.props.deleteRoom}/>
: null
}
</Grid.Column>
</Grid.Row>
</Grid>
</ Menu.Item>
)
}) : null
}
<Menu.Item
name='newM'
active={activeItem === 'Plus'}
active={this.state.activeItem === 'newM'}
>
<Grid>
<Grid.Row centered onClick={this.showForm} name='new'>
<Grid.Row centered name='new'>
<ModalWindow type="new" addRoom={this.props.addRoom}/>
</Grid.Row>
</Grid>

View file

@ -0,0 +1,20 @@
import _ from "lodash";
import React, { Component } from "react";
import HeaderController from "./../components/HeaderController";
import { render } from "react-dom";
import {
Container,
Icon,
Image,
Menu,
Sidebar,
Responsive
} from "semantic-ui-react";
export default class TestHeaderController extends React.Component {
render () {
return (
<HeaderController/>
)
}
}