Skip to content

Commit

Permalink
Merge pull request #362 from simpleiot/cbrake/master
Browse files Browse the repository at this point in the history
better rule node ID copying
  • Loading branch information
cbrake committed May 31, 2022
2 parents a50a7f3 + 84b4205 commit 18e8835
Show file tree
Hide file tree
Showing 30 changed files with 322 additions and 57 deletions.
23 changes: 23 additions & 0 deletions CHANGELOG.md
Expand Up @@ -11,6 +11,29 @@ For more details or to discuss releases, please visit the

## [Unreleased]

## [[0.2.0] - 2022-05-31](https://github.com/simpleiot/simpleiot/releases/tag/v0.2.0)

(implemented in PR #362)

- UI: fix sorting of Rule child nodes
- highlight rule actions when active #266
- better linking of nodes for rules #251
- display clipboard contents at top of screen
- update elm/virtual-dom to 1.0.3 (helps
[prevent xss attacks](https://jfmengels.net/virtual-dom-security-patch/))

This release improves the process of linking nodes to rule actions or
conditions. In the past, the system clipboard was used and you had to paste the
system clipboard contents into the Node ID field of rule conditions and actions.
Now, when you a copy a node, the SIOT frontend has its own clipboard and a past
button is displayed below the Node ID fields for easy pasting the node ID.
Additionally, the node description is displayed below the Node ID field so you
can easily tell which node the ID is referring to.

A [video is available](https://youtu.be/tqbLZ9CSzRU) which illustrates how node
IDs can now be copied and pasted.
[docs](https://docs.simpleiot.org/docs/user/rules.html) are also updated.

## [[0.1.0] - 2022-05-13](https://github.com/simpleiot/simpleiot/releases/tag/v0.1.0)

- docs: add list of supported devices to install
Expand Down
32 changes: 25 additions & 7 deletions docs/ref/frontend.md
@@ -1,11 +1,5 @@
# Frontend

## Reference Implementation

The reference Simple IoT frontend is implemented in Elm and located in the
[`frontend/`](https://github.com/simpleiot/simpleiot/tree/master/frontend)
directory.

## SIOT JavaScript library using NATS over WebSockets

This is a JavaScript library avaiable in the
Expand All @@ -29,7 +23,31 @@ This library is also published on NPM (in the near future).

(see [#357](https://github.com/simpleiot/simpleiot/pull/357))

## Creating Custom Icons
(Note, we are not currently using this yet in the SIOT frontend -- we still poll
the backend over REST and fetch the entire node tree, but we are building out
infrastructure so we don't have to do this.)

## Elm Reference Implementation

The reference Simple IoT frontend is implemented in Elm and located in the
[`frontend/`](https://github.com/simpleiot/simpleiot/tree/master/frontend)
directory.

### Code Structure

The frontend is based on [elm-spa](https://www.elm-spa.dev/), and is split into
the following directories:

- **Api**: contains core data structures and API code to communicate with
backend (currently REST).
- **Pages**: the various pages of the application
- **Components**: each node type has a separate module that is used to render
it. `NodeOptions.elm` contains a struct that is used to pass options into the
component views.
- **UI**: Various UI pieces we used
- **Utils**: Code that does not fit anywhere else (time, etc)

### Creating Custom Icons

SIOT icons are 24x24px pixels (based on feather icon format). One way to create
them is to:
Expand Down
Binary file modified docs/user/images/group-user.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/user/images/icon-arrow.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/user/images/icon-copy.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/user/images/icon-delete.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/user/images/icon-dot.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/user/images/icon-paste.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/user/images/joe-nodes.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/user/images/node-add.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/user/images/node-edit.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/user/images/nodes.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/user/images/paste-options.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/user/images/rule-copy-paste-node-id.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/user/images/rules.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 9 additions & 1 deletion docs/user/rules.md
Expand Up @@ -15,7 +15,15 @@ device nodes (for instance device offline).
In the below configuration, a change in the SBC propagates up the node tree,
thus both the `D5 on rule` or the `Device offline rule` are eligible to be run.

<img src="images/rules.png" alt="rules" style="zoom:50%;" />
![rules](images/rules.png)

## Node linking

Both conditions and actions can be linked to a node ID. If you copy a node, its
ID is stored in a virtual clipboard and displayed at the top of the screen. You
can then paste this node ID into the Node ID field in a condition or action.

![rule-linking](images/rule-copy-paste-node-id.png)

## Conditions

Expand Down
21 changes: 9 additions & 12 deletions docs/user/ui.md
Expand Up @@ -11,29 +11,27 @@ After Simple IoT is started, a web application is available on port `:8080`
(default user/pass is `admin@admin.com`/`admin`), you will be presented with a
tree of nodes.

<img src="images/nodes.png" alt="nodes" style="zoom: 67%;" />
![nodes](images/nodes.png)

The `Node` is the base unit of configuration. Each node contains `Points` which
describe various attributes of a node. When you expand a node, the information
you see is a rendering of the point data in the node.

You can expand/collapse child nodes by clicking on the arrow
<img src="images/icon-arrow.png" alt="arrow" style="zoom: 50%;" /> to the left
of a node.
![arrow](images/icon-arrow.png) to the left of a node.

You can expand/edit node details by clicking on the dot
<img src="images/icon-dot.png" alt="dot" style="zoom: 50%;" /> to the left of a
node.
![dot](images/icon-dot.png) to the left of a node.

<img src="images/node-edit.png" alt="node edit" style="zoom: 50%;" />
![node edit](images/node-edit.png)

## Adding nodes

Child nodes can be added to a node by clicking on the dot to expand the node,
then clicking on the plus icon. A list of available nodes to add will then be
displayed:

<img src="images/node-add.png" alt="add node" style="zoom:67%;" />
![node add](images/node-add.png)

Some nodes are populated automatically if a new device is discovered, or a
downstream device starts sending data.
Expand All @@ -43,15 +41,14 @@ downstream device starts sending data.
Simple IoT provides the ability to re-arrange and organize your node structure.

To delete a node, expand it, and then press the delete
<img src="images/icon-delete.png" style="zoom:33%;" /> icon.
![icon delete](images/icon-delete.png) icon.

To move or copy a node, expand it and press the copy
<img src="images/icon-copy.png" style="zoom: 33%;" /> icon. Then expand the
destination node and press the paste
<img src="images/icon-paste.png" style="zoom:33%;" /> icon. You will then be
![copy icon](images/icon-copy.png) icon. Then expand the destination node and
press the paste ![paste icon](images/icon-paste.png) icon. You will then be
presented with the following options:

<img src="images/paste-options.png" alt="paste options" style="zoom: 33%;" />
![paste options](images/paste-options.png)

- **move** - moves a node to new location
- **mirror** - is useful if you want a user or device to be a member of multiple
Expand Down
4 changes: 2 additions & 2 deletions docs/user/users-groups.md
Expand Up @@ -6,8 +6,8 @@ children. In the below example, `Joe` has access to the `SBC` device because
both `Joe` and `SBC` are members of the `Site 1` group. `Joe` does have access
to the `root` node.

<img src="images/group-user.png" alt="group user" style="zoom: 50%;" />
![group user](images/group-user.png)

If `Joe` logs in, the following view will be presented:

<img src="images/joe-nodes.png" alt="joe nodes" style="zoom: 50%;" />
![joe nodes](images/joe-nodes.png)
2 changes: 1 addition & 1 deletion frontend/elm.json
Expand Up @@ -29,7 +29,7 @@
"indirect": {
"elm/bytes": "1.0.8",
"elm/file": "1.0.5",
"elm/virtual-dom": "1.0.2",
"elm/virtual-dom": "1.0.3",
"elm-community/maybe-extra": "5.2.0",
"justinmimbs/date": "3.2.1"
}
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/Api/Node.elm
@@ -1,5 +1,6 @@
module Api.Node exposing
( Node
, NodeView
, copy
, delete
, description
Expand Down Expand Up @@ -146,6 +147,17 @@ type alias Node =
}


type alias NodeView =
{ node : Node
, feID : Int
, parentID : String
, hasChildren : Bool
, expDetail : Bool
, expChildren : Bool
, mod : Bool
}


type alias NodeCmd =
{ cmd : String
, detail : String
Expand Down
75 changes: 70 additions & 5 deletions frontend/src/Components/NodeAction.elm
Expand Up @@ -2,12 +2,14 @@ module Components.NodeAction exposing (view)

import Api.Node as Node
import Api.Point as Point
import Components.NodeOptions exposing (NodeOptions, oToInputO)
import Components.NodeOptions exposing (CopyMove(..), NodeOptions, findNode, oToInputO)
import Element exposing (..)
import Element.Background as Background
import Element.Border as Border
import Element.Font as Font
import UI.Icon as Icon
import UI.NodeInputs as NodeInputs
import UI.Style exposing (colors)
import UI.Style as Style
import UI.ViewIf exposing (viewIf)


Expand Down Expand Up @@ -50,18 +52,39 @@ view o =

valueType =
Point.getText o.node.points Point.typeValueType ""

nodeId =
Point.getText o.node.points Point.typeID ""

active =
Point.getBool o.node.points Point.typeActive ""

descBackgroundColor =
if active then
Style.colors.blue

else
Style.colors.none

descTextColor =
if active then
Style.colors.white

else
Style.colors.black
in
column
[ width fill
, Border.widthEach { top = 2, bottom = 0, left = 0, right = 0 }
, Border.color colors.black
, Border.color Style.colors.black
, spacing 6
]
<|
wrappedRow [ spacing 10 ]
[ icon
, text <|
Point.getText o.node.points Point.typeDescription ""
, el [ Background.color descBackgroundColor, Font.color descTextColor ] <|
text <|
Point.getText o.node.points Point.typeDescription ""
]
:: (if o.expDetail then
[ textInput Point.typeDescription "Description" ""
Expand All @@ -78,6 +101,48 @@ view o =
, ( Point.typeValueSet, "set value (use for remote devices)" )
]
, viewIf actionSetValue <| textInput Point.typeID "Node ID" ""
, if nodeId /= "" then
let
nodeDesc =
case findNode o.nodes nodeId of
Just node ->
el [ Background.color Style.colors.ltblue ] <|
text <|
"("
++ Node.getBestDesc node
++ ")"

Nothing ->
el [ Background.color Style.colors.orange ] <| text "(node not found)"
in
el [ Font.italic, paddingEach { top = 0, right = 0, left = 170, bottom = 0 } ] <|
nodeDesc

else
Element.none
, case o.copy of
CopyMoveNone ->
Element.none

Copy id _ desc ->
if nodeId /= id then
let
label =
row
[ spacing 10 ]
[ text <| "paste ID for node: "
, el
[ Font.italic
, Background.color Style.colors.ltblue
]
<|
text desc
]
in
NodeInputs.nodePasteButton opts label Point.typeID id

else
Element.none
, viewIf actionSetValue <|
optionInput Point.typeValueType
"Point Value Type"
Expand Down
51 changes: 48 additions & 3 deletions frontend/src/Components/NodeCondition.elm
@@ -1,14 +1,15 @@
module Components.NodeCondition exposing (view)

import Api.Node as Node
import Api.Point as Point
import Components.NodeOptions exposing (NodeOptions, oToInputO)
import Components.NodeOptions exposing (CopyMove(..), NodeOptions, findNode, oToInputO)
import Element exposing (..)
import Element.Background as Background
import Element.Border as Border
import Element.Font as Font
import UI.Icon as Icon
import UI.NodeInputs as NodeInputs
import UI.Style as Style exposing (colors)
import UI.Style as Style


view : NodeOptions msg -> Element msg
Expand Down Expand Up @@ -49,7 +50,7 @@ view o =
column
[ width fill
, Border.widthEach { top = 2, bottom = 0, left = 0, right = 0 }
, Border.color colors.black
, Border.color Style.colors.black
, spacing 6
]
<|
Expand Down Expand Up @@ -115,6 +116,9 @@ pointValue o labelWidth =
conditionValueType =
Point.getText o.node.points Point.typeValueType ""

nodeId =
Point.getText o.node.points Point.typeID ""

operators =
case conditionValueType of
"number" ->
Expand All @@ -138,6 +142,47 @@ pointValue o labelWidth =
, spacing 6
]
[ textInput Point.typeID "Node ID" ""
, if nodeId /= "" then
let
nodeDesc =
case findNode o.nodes nodeId of
Just node ->
el [ Background.color Style.colors.ltblue ] <|
text <|
"("
++ Node.getBestDesc node
++ ")"

Nothing ->
el [ Background.color Style.colors.orange ] <| text "(node not found)"
in
el [ Font.italic, paddingEach { top = 0, right = 0, left = 170, bottom = 0 } ] <|
nodeDesc

else
Element.none
, case o.copy of
CopyMoveNone ->
Element.none

Copy id _ desc ->
if nodeId /= id then
let
label =
row [ spacing 10 ]
[ text <| "paste ID for node: "
, el
[ Font.italic
, Background.color Style.colors.ltblue
]
<|
text desc
]
in
NodeInputs.nodePasteButton opts label Point.typeID id

else
Element.none
, optionInput Point.typePointType
"Point Type"
[ ( Point.typeValue, "value" )
Expand Down

0 comments on commit 18e8835

Please sign in to comment.