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: stages:
- build - build
- test - test
- deploy
cache: cache:
paths: paths:
@ -22,3 +23,22 @@ testing_testing:
script: script:
- cd smart-hut - 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() { logout() {
console.log("logout")
this.setState({ this.setState({
loggedIn : false, loggedIn : false,
}); });
localStorage.removeItem("token"); localStorage.removeItem("token");
this.props.history.push("/");
}; };
render() { render() {

View file

@ -59,6 +59,28 @@ export var call = {
return err; 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) { createRoom: function(data, headers) {
return axios.post(config + 'room', data, { headers: { Authorization : "Bearer " + tkn } }) return axios.post(config + 'room', data, { headers: { Authorization : "Bearer " + tkn } })
.then(res => { .then(res => {

View file

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

View file

@ -59,16 +59,42 @@ const devices = [
]; ];
class Panel extends Component { export default class DevicePanel extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
devices: devices, editMode : false,
devices : this.props.devices,
};
}
editMode: false, editModeController = (e) => this.setState((prevState) => ({ editMode: !prevState.editMode }));
openSettingsModal: false,
settingsDeviceId: null, 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]; device[prop] = newSettings[prop];
} }
}
}
}
return null;
});
this.forceUpdate();
};
openModal = (settingsDeviceId) => { render() {
this.setState(prevState => ({ return (
openSettingsModal: !prevState.openSettingsModal, <div style={panelStyle}>
settingsDeviceId: settingsDeviceId <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 ?
render() { this.props.devices.map((e, i) => {
const edit = { return (
mode: this.state.editMode, <Grid.Column key={i}>
openModal: this.openModal, <DeviceType type={e.kind} onChangeData={this.changeDeviceData} device={e} edit={this.state.editMode}/>
}; </Grid.Column>
return ( )
<div style={panelStyle}> })
<button style={editButtonStyle} onClick={this.editModeController}>Edit</button> :
<Grid doubling columns={5} divided="vertically"> null
{this.state.openSettingsModal ? }
<SettingsModal openModal={this.openModal} device={this.state.devices.filter(d => d.id === this.state.settingsDeviceId)[0]}/> : ""} <Grid.Column>
<Grid.Column> <NewDevice/>
<SmartPlug onChangeData={this.changeDeviceData} device={devices[4]} edit={edit}/> </Grid.Column>
</Grid.Column> </Grid>
<Grid.Column> </div>
<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 (
<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 // As far as I am concern, even though this dimmer doesn't have state, internally it's needed
constructor(props){ constructor(props){
super(props); super(props);

View file

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

View file

@ -4,18 +4,24 @@ import Navbar from './Navbar'
import MyHeader from '../components/HeaderController' import MyHeader from '../components/HeaderController'
import { call } from '../client_server'; import { call } from '../client_server';
import {Button} from 'semantic-ui-react'; import { Grid } from 'semantic-ui-react'
import { Menu } from 'semantic-ui-react' /*
import { Grid, Image, Icon } 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{ export default class Dashboard extends Component{
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
rooms: [], rooms: [],
activeItem: "Home", activeItem: -1,
tkn: this.props.tkn devices: [],
tkn: this.props.tkn,
}; };
this.addRoom = this.addRoom.bind(this); this.addRoom = this.addRoom.bind(this);
@ -25,11 +31,17 @@ export default class Dashboard extends Component{
componentDidMount() { componentDidMount() {
call.getAllRooms(this.props.tkn) call.getAllRooms(this.props.tkn)
.then(res => { .then(res => {
res.data.forEach((e) => { this.setState({
this.setState(state => ({ rooms: res.data
rooms: state.rooms.concat([e]) });
})); }).catch(err => {
}); console.log(err);
});
call.getAllDevices(this.props.tkn)
.then(res => {
this.setState({
devices: res.data
});
}).catch(err => { }).catch(err => {
console.log(err); console.log(err);
}); });
@ -50,11 +62,25 @@ export default class Dashboard extends Component{
}; };
deleteRoom(id) { 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 //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 () { render () {
@ -65,8 +91,8 @@ export default class Dashboard extends Component{
<Grid.Row color='black'> <Grid.Row color='black'>
{/*TODO change this back {/*TODO change this back
<Grid.Column> <Grid.Column>
<MyHeader /> <MyHeader logout={this.props.logout} />
</Grid.Column>*/} </Grid.Column>
</Grid.Row> </Grid.Row>
<Grid.Row color='black'> <Grid.Row color='black'>
<Grid.Column width={3}> <Grid.Column width={3}>
@ -74,7 +100,7 @@ export default class Dashboard extends Component{
</Grid.Column> </Grid.Column>
<Grid.Column width={13}> <Grid.Column width={13}>
<DevicePanel /> <DevicePanel devices={this.state.devices} />
</Grid.Column> </Grid.Column>
</Grid.Row> </Grid.Row>
</Grid> </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 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"; import {Link } from "react-router-dom";
@ -7,13 +7,29 @@ export default class FourOhFour extends Component {
render() { render() {
return ( return (
<Message> <div style={{height : "110vh", background: '#1b1c1d'}}>
<Message.Header>404 Page Not Found</Message.Header> <Grid centered>
<p> <Grid.Row>
Hey what are you doing here? <Header as='h1'>404</Header>
Go back to our homepage <Link to="/"/> </Grid.Row>
</p> <Grid.Row>
</Message> <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); super(props);
this.state = { this.state = {
activeItem: 'Home', activeItem: 'Home',
edited: "" edited: "",
editMode : false
} }
} }
showForm = (event) => { editModeController = (e) => {
console.log(event); this.setState((prevState) => ({ editMode: !prevState.editMode }));
if (event === "new"){ };
console.log("funziona");
return (<ModalWindow type="new"/>)
}
}
handleClick = (e, { i }) => { handleClick = (e, {id}) => {
this.setState({ activeItem: e.name, console.log(id);
edited: i this.setState({ activeItem: id,
}); });
this.props.handleItemClick(e) this.props.handleItemClick(id)
} }
render(){ render(){
const { activeItem } = this.state //const { activeItem } = this.state
return ( return (
<div> <div>
<Segment.Group> <Segment.Group>
@ -40,8 +37,9 @@ class Navbar extends Component {
<Grid.Row color='black'> <Grid.Row color='black'>
<Menu inverted fluid vertical> <Menu inverted fluid vertical>
<Menu.Item <Menu.Item
key={-1}
name='Home' name='Home'
active={activeItem === 'Home'} active={this.state.activeItem === 'Home'}
onClick={this.handleClick} onClick={this.handleClick}
> >
<Grid> <Grid>
@ -50,7 +48,7 @@ class Navbar extends Component {
<Icon name="home" size="small"/> <Icon name="home" size="small"/>
</Grid.Column> </Grid.Column>
<Grid.Column> <Grid.Column>
Home HOME
</Grid.Column> </Grid.Column>
</Grid.Row> </Grid.Row>
</Grid> </Grid>
@ -64,38 +62,38 @@ class Navbar extends Component {
id={e.id} id={e.id}
key={i} key={i}
name={e.name} name={e.name}
active={activeItem === e.name} active={this.state.activeItem === e.id}
onClick={this.handleClick} onClick={this.handleClick}
> >
<Grid> <Grid>
<Grid.Row> <Grid.Row>
<Grid.Column width={1}> <Grid.Column>
<Icon name={e.icon} size="small"/> <Icon name={e.icon} size="small"/>
</Grid.Column> </Grid.Column>
<Grid.Column width={12}> <Grid.Column width={8}>
{e.name} {e.name}
</Grid.Column> </Grid.Column>
<Grid.Column width={1}> <Grid.Column floated="right">
<Icon name="pencil" size="small"/> {this.state.edit ?
<ModalWindow type="modify" idRoom={e} modifyRoom={this.props.updateRoom} deleteRoom={this.props.deleteRoom}/>
: null
}
</Grid.Column> </Grid.Column>
</Grid.Row> </Grid.Row>
</Grid> </Grid>
</ Menu.Item> </ Menu.Item>
) )
}) : null }) : null
} }
<Menu.Item <Menu.Item
name='newM' name='newM'
active={activeItem === 'Plus'} active={this.state.activeItem === 'newM'}
> >
<Grid> <Grid>
<Grid.Row centered onClick={this.showForm} name='new'> <Grid.Row centered name='new'>
<ModalWindow type="new" addRoom={this.props.addRoom}/> <ModalWindow type="new" addRoom={this.props.addRoom}/>
</Grid.Row> </Grid.Row>
</Grid> </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/>
)
}
}