From f74b83763281495f76200e99056f96491045e535 Mon Sep 17 00:00:00 2001 From: Pierre-Gilles Leymarie Date: Sat, 21 Mar 2020 16:32:53 +0100 Subject: [PATCH] Multiple UI fixes + implement umzug so that DB is migrated in Gladys directly (#711) * fix #664 remove reboot button * fix #657 - no page on get device * Fix #669: edit room name * Fix #661: no value for motion on dashboard * Fix #679: execute DB migrations on startup * fix #656: dashboard rooms by houses * Map - Fit zoom to markers * Prettier on Map.jsx * prettier * Prettier --' * Remove pagination everywhere on get device request * Don't send to_update property to room update route * House & Room name should be between 1 & 40 characters * Edit house: Add more validation errors in UI * Fix linting * remove front flag * Fix margin problems with room names * Run tests in a RAM db * Fix codecov.yml * Fix Maps: markerArray should not be a global variable * Eslint should check .jsx files * Update README * Change Maps markerArray lifecycle Co-authored-by: atrovato <1839717+atrovato@users.noreply.github.com> Co-authored-by: Vincent KULAK --- README.md | 6 +-- codecov.yml | 6 +-- front/package.json | 2 +- .../dashboard/edit-boxes/editDevicesInRoom.js | 17 ------- .../edit-boxes/editTemperatureInRoom.js | 17 ------- front/src/actions/house.js | 33 +++++++++++-- .../boxs/device-in-room/EditDeviceInRoom.jsx | 47 +++++++------------ .../device-features/SensorDeviceFeature.jsx | 8 ++-- .../EditRoomTemperatureBox.jsx | 25 ++++------ .../documentation/DeviceConfigurationLink.jsx | 2 +- front/src/components/house/EditHouse.jsx | 41 +++++----------- .../components/house/EditHouseComponent.jsx | 4 ++ front/src/components/house/EditRoom.jsx | 45 ++++++++++++++++++ front/src/components/house/RoomSelector.jsx | 45 ++++++++++++++++++ front/src/config/i18n/en.json | 5 +- .../all/mqtt/device-page/actions.js | 21 ++------- .../integration/all/mqtt/device-page/index.js | 2 +- .../all/philips-hue/device-page/actions.js | 5 +- .../integration/all/rtsp-camera/actions.js | 22 +++------ .../integration/all/rtsp-camera/index.js | 5 +- .../routes/integration/all/xiaomi/actions.js | 5 +- .../all/zwave/node-page/actions.js | 21 ++------- .../integration/all/zwave/node-page/index.js | 2 +- front/src/routes/map/Map.jsx | 8 ++++ .../settings-system/SettingsSystemPage.jsx | 25 ---------- front/src/utils/consts.js | 3 +- server/config/config.js | 2 +- server/lib/index.js | 3 ++ server/models/house.js | 3 ++ server/models/index.js | 17 +++++++ server/models/room.js | 3 ++ server/package.json | 8 ++-- server/test/bootstrap.test.js | 6 +++ server/test/lib/house/house.test.js | 12 +++++ server/test/lib/room/room.test.js | 12 +++++ 35 files changed, 271 insertions(+), 217 deletions(-) create mode 100644 front/src/components/house/EditRoom.jsx create mode 100644 front/src/components/house/RoomSelector.jsx diff --git a/README.md b/README.md index 6d057cce07..745d4f5147 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ This is just a simple scenario, possibilities with Gladys are just endless. Don' Right now we are migrating from Gladys 3 to Gladys 4, a new release rewrote from scratch. If you want to install Gladys 3, visit [our website](https://gladysassistant.com). -To install Gladys 4 Alpha, read the following instructions. +To install Gladys 4 Beta, read the following instructions. We use Docker to deploy Gladys 4, so it's easy to install it on any system. @@ -69,7 +69,7 @@ It's only thanks to donation from the community that my open-source work is sust To support the project, you can: -- Support monthly, 9.99€/month with the [Gladys Community Package](https://gladysassistant.com/gladys-community-package). +- Support monthly, 9.99€/month with [Gladys Plus](https://gladysassistant.com/pricing). - Donate with Bitcoin: 3KQiX1FtbdXLXPH9UfLSyuzRMDRGY52EiA ## Links @@ -82,4 +82,4 @@ To support the project, you can: ## Copyright & License -Copyright (c) 2013-2019 Gladys Assistant - Released under the [Apache 2.0 License](https://github.com/gladysassistant/Gladys/blob/master/LICENSE). +Copyright (c) 2013-2020 Gladys Assistant - Released under the [Apache 2.0 License](https://github.com/gladysassistant/Gladys/blob/master/LICENSE). diff --git a/codecov.yml b/codecov.yml index 108f84983c..3de8f17ed7 100644 --- a/codecov.yml +++ b/codecov.yml @@ -3,12 +3,10 @@ coverage: project: server: target: 90% - flags: server + flags: + - server flags: server: paths: - server - front: - paths: - - front diff --git a/front/package.json b/front/package.json index ecfc6d2fa7..de1e4eb6c6 100644 --- a/front/package.json +++ b/front/package.json @@ -7,7 +7,7 @@ "build": "cross-env NODE_ENV=production preact build --template src/template.html --no-prerender", "serve": "npm run build && preact serve", "dev": "preact watch -p 1444 --template src/template.html", - "eslint": "eslint src --ext .json --ext .js", + "eslint": "eslint src --ext .json --ext .js --ext .jsx", "prettier-check": "prettier --check '**/*.js' '**/*.jsx' '**/*.json'", "prettier": "prettier --write '**/*.js' '**/*.jsx' '**/*.json'", "test": "jest --coverage" diff --git a/front/src/actions/dashboard/edit-boxes/editDevicesInRoom.js b/front/src/actions/dashboard/edit-boxes/editDevicesInRoom.js index ed0073c927..7ddf1e0592 100644 --- a/front/src/actions/dashboard/edit-boxes/editDevicesInRoom.js +++ b/front/src/actions/dashboard/edit-boxes/editDevicesInRoom.js @@ -1,26 +1,9 @@ -import { RequestStatus } from '../../../utils/consts'; import createBoxActions from '../boxActions'; function createActions(store) { const boxActions = createBoxActions(store); const actions = { - async getRooms(state) { - store.setState({ - DashboardEditDeviceInRoomStatus: RequestStatus.Getting - }); - try { - const rooms = await state.httpClient.get('/api/v1/room'); - store.setState({ - rooms, - DashboardEditDeviceInRoomStatus: RequestStatus.Success - }); - } catch (e) { - store.setState({ - DashboardEditDeviceInRoomStatus: RequestStatus.Error - }); - } - }, updateBoxRoom(state, x, y, room) { boxActions.updateBoxConfig(state, x, y, { room diff --git a/front/src/actions/dashboard/edit-boxes/editTemperatureInRoom.js b/front/src/actions/dashboard/edit-boxes/editTemperatureInRoom.js index bb56ddcb03..7ddf1e0592 100644 --- a/front/src/actions/dashboard/edit-boxes/editTemperatureInRoom.js +++ b/front/src/actions/dashboard/edit-boxes/editTemperatureInRoom.js @@ -1,26 +1,9 @@ -import { RequestStatus } from '../../../utils/consts'; import createBoxActions from '../boxActions'; function createActions(store) { const boxActions = createBoxActions(store); const actions = { - async getRooms(state) { - store.setState({ - DashboardEditTemperatureInRoomStatus: RequestStatus.Getting - }); - try { - const rooms = await state.httpClient.get('/api/v1/room'); - store.setState({ - rooms, - DashboardEditTemperatureInRoomStatus: RequestStatus.Success - }); - } catch (e) { - store.setState({ - DashboardEditTemperatureInRoomStatus: RequestStatus.Error - }); - } - }, updateBoxRoom(state, x, y, room) { boxActions.updateBoxConfig(state, x, y, { room diff --git a/front/src/actions/house.js b/front/src/actions/house.js index df86d08801..a8be2c9c26 100644 --- a/front/src/actions/house.js +++ b/front/src/actions/house.js @@ -121,6 +121,25 @@ function createActions(store) { }); store.setState(newState); }, + editRoom(state, houseIndex, roomIndex, property, value) { + const newState = update(state, { + houses: { + [houseIndex]: { + rooms: { + [roomIndex]: { + [property]: { + $set: value + }, + to_update: { + $set: true + } + } + } + } + } + }); + store.setState(newState); + }, addHouse(state) { const newState = update(state, { houses: { @@ -154,10 +173,12 @@ function createActions(store) { const promises = house.rooms.map(async room => { if (room.to_delete) { - return state.httpClient.delete(`/api/v1/room/${room.selector}`, room); + return state.httpClient.delete(`/api/v1/room/${room.selector}`); } if (!room.id) { return state.httpClient.post(`/api/v1/house/${houseCreatedOrUpdated.selector}/room`, room); + } else if (room.to_update) { + return state.httpClient.patch(`/api/v1/room/${room.selector}`, { name: room.name }); } return room; }); @@ -193,8 +214,8 @@ function createActions(store) { store.setState(newState); } catch (e) { const status = get(e, 'response.status'); - const errorValue = get(e, 'response.data.error.value'); - if (status === 409 && errorValue === 'room') { + const url = get(e, 'response.config.url'); + if (status === 409 && url.endsWith('/room')) { store.setState({ houseUpdateStatus: { [house.id]: RequestStatus.RoomConflictError @@ -206,6 +227,12 @@ function createActions(store) { [house.id]: RequestStatus.ConflictError } }); + } else if (status === 422 && url.includes('/room/')) { + store.setState({ + houseUpdateStatus: { + [house.id]: RequestStatus.RoomValidationError + } + }); } else if (status === 422) { store.setState({ houseUpdateStatus: { diff --git a/front/src/components/boxs/device-in-room/EditDeviceInRoom.jsx b/front/src/components/boxs/device-in-room/EditDeviceInRoom.jsx index c83ae5655a..148e33d246 100644 --- a/front/src/components/boxs/device-in-room/EditDeviceInRoom.jsx +++ b/front/src/components/boxs/device-in-room/EditDeviceInRoom.jsx @@ -1,38 +1,27 @@ import { Component } from 'preact'; import { connect } from 'unistore/preact'; -import actions from '../../../actions/dashboard/edit-boxes/editDevicesInRoom'; -import BaseEditBox from '../baseEditBox'; -const updateBoxRoom = (updateBoxRoomFunc, x, y) => e => { - updateBoxRoomFunc(x, y, e.target.value); -}; +import BaseEditBox from '../baseEditBox'; +import RoomSelector from '../../house/RoomSelector'; -const EditDevicesInRoom = ({ children, ...props }) => ( - -
- - -
-
-); +import actions from '../../../actions/dashboard/edit-boxes/editDevicesInRoom'; -@connect('rooms', actions) -class EditDeviceInRoomComponent extends Component { - componentDidMount() { - this.props.getRooms(); - } +@connect('', actions) +class EditDeviceInRoom extends Component { + updateBoxRoom = room => { + this.props.updateBoxRoom(this.props.x, this.props.y, room.selector); + }; - render(props, {}) { - return ; + render(props) { + return ( + +
+ + +
+
+ ); } } -export default EditDeviceInRoomComponent; +export default EditDeviceInRoom; diff --git a/front/src/components/boxs/device-in-room/device-features/SensorDeviceFeature.jsx b/front/src/components/boxs/device-in-room/device-features/SensorDeviceFeature.jsx index b35e482f89..eba8037256 100644 --- a/front/src/components/boxs/device-in-room/device-features/SensorDeviceFeature.jsx +++ b/front/src/components/boxs/device-in-room/device-features/SensorDeviceFeature.jsx @@ -48,9 +48,11 @@ const SensorDeviceType = ({ children, ...props }) => ( )} {props.deviceFeature.category === DEVICE_FEATURE_CATEGORIES.MOTION_SENSOR && ( - {dayjs(props.deviceFeature.last_value_changed) - .locale(props.user.language) - .fromNow()} + {!props.deviceFeature.last_value_changed && } + {props.deviceFeature.last_value_changed && + dayjs(props.deviceFeature.last_value_changed) + .locale(props.user.language) + .fromNow()} )} diff --git a/front/src/components/boxs/room-temperature/EditRoomTemperatureBox.jsx b/front/src/components/boxs/room-temperature/EditRoomTemperatureBox.jsx index 984cbbaf01..29019c5ba3 100644 --- a/front/src/components/boxs/room-temperature/EditRoomTemperatureBox.jsx +++ b/front/src/components/boxs/room-temperature/EditRoomTemperatureBox.jsx @@ -4,8 +4,10 @@ import { Text } from 'preact-i18n'; import actions from '../../../actions/dashboard/edit-boxes/editTemperatureInRoom'; import BaseEditBox from '../baseEditBox'; -const updateBoxRoom = (updateBoxRoomFunc, x, y) => e => { - updateBoxRoomFunc(x, y, e.target.value); +import RoomSelector from '../../house/RoomSelector'; + +const updateBoxRoom = (updateBoxRoomFunc, x, y) => room => { + updateBoxRoomFunc(x, y, room.selector); }; const EditRoomTemperatureBox = ({ children, ...props }) => ( @@ -14,25 +16,16 @@ const EditRoomTemperatureBox = ({ children, ...props }) => ( - + ); -@connect('rooms', actions) +@connect('', actions) class EditRoomTemperatureBoxComponent extends Component { - componentDidMount() { - this.props.getRooms(); - } - render(props, {}) { return ; } diff --git a/front/src/components/documentation/DeviceConfigurationLink.jsx b/front/src/components/documentation/DeviceConfigurationLink.jsx index 55527f6164..9a3d921e0d 100644 --- a/front/src/components/documentation/DeviceConfigurationLink.jsx +++ b/front/src/components/documentation/DeviceConfigurationLink.jsx @@ -1,4 +1,3 @@ -import { Text } from 'preact-i18n'; import cx from 'classnames'; const documentationURL = 'https://documentation.gladysassistant.com'; @@ -6,6 +5,7 @@ const documentationURL = 'https://documentation.gladysassistant.com'; const DeviceConfigurationLink = ({ children, documentKey, user, linkClass }) => ( () => { - removeRoom(index); -}; +import EditRoom from './EditRoom'; const EditHouse = ({ children, ...props }) => (
(
)} - {props.houseUpdateStatus === RequestStatus.RoomConflictError && ( -
- -
- )} {props.houseUpdateStatus === RequestStatus.NetworkError && (
@@ -70,26 +62,19 @@ const EditHouse = ({ children, ...props }) => ( -
+ {props.houseUpdateStatus === RequestStatus.RoomConflictError && ( +
+ +
+ )} + {props.houseUpdateStatus === RequestStatus.RoomValidationError && ( +
+ +
+ )} +
diff --git a/front/src/components/house/EditHouseComponent.jsx b/front/src/components/house/EditHouseComponent.jsx index fb351811b5..bbe9cd3754 100644 --- a/front/src/components/house/EditHouseComponent.jsx +++ b/front/src/components/house/EditHouseComponent.jsx @@ -20,6 +20,9 @@ class EditHouseComponent extends Component { removeRoom = roomIndex => { this.props.removeRoom(this.props.houseIndex, roomIndex); }; + editRoom = (roomIndex, property, value) => { + this.props.editRoom(this.props.houseIndex, roomIndex, property, value); + }; saveHouse = async () => { this.setState({ loading: true @@ -72,6 +75,7 @@ class EditHouseComponent extends Component { newRoomName={newRoomName} addRoom={this.addRoom} removeRoom={this.removeRoom} + editRoom={this.editRoom} saveHouse={this.saveHouse} onKeyPressRoomInput={this.onKeyPressRoomInput} wantToDeleteHouse={wantToDeleteHouse} diff --git a/front/src/components/house/EditRoom.jsx b/front/src/components/house/EditRoom.jsx new file mode 100644 index 0000000000..6ee8d333cc --- /dev/null +++ b/front/src/components/house/EditRoom.jsx @@ -0,0 +1,45 @@ +import { Component } from 'preact'; + +class EditRoom extends Component { + removeRoomLocal = () => { + this.props.removeRoom(this.props.index); + }; + + editRoomLocal = e => { + e.preventDefault(); + + this.props.editRoom(this.props.index, 'name', e.target.value); + }; + + constructor(props) { + super(props); + this.state = { initialName: this.props.room.name }; + } + + render({ room }, { initialName }) { + if (room.to_delete === true) { + return null; + } + + return ( +
+
+ +
+
+ +
+
+
+
+ ); + } +} + +export default EditRoom; diff --git a/front/src/components/house/RoomSelector.jsx b/front/src/components/house/RoomSelector.jsx new file mode 100644 index 0000000000..14fc7baf0e --- /dev/null +++ b/front/src/components/house/RoomSelector.jsx @@ -0,0 +1,45 @@ +import { Component } from 'preact'; +import { connect } from 'unistore/preact'; +import Select from 'react-select'; +import actions from '../../actions/house'; + +@connect('houses', actions) +class RoomSelector extends Component { + updateSelection = option => { + this.props.updateRoomSelection(option.room); + }; + + componentDidMount = () => { + this.props.getHouses(); + }; + + componentWillReceiveProps = newProps => { + let selectedRoom; + const houseOptions = newProps.houses.map(house => { + return { + label: house.name, + options: house.rooms.map(room => { + const option = { + label: room.name, + value: room.selector, + room + }; + + if (newProps.selectedRoom === room.selector) { + selectedRoom = option; + } + + return option; + }) + }; + }); + + this.setState({ houseOptions, selectedRoom }); + }; + + render({}, { selectedRoom, houseOptions }) { + return