Browse Source

Started UI improvements

update-deps
Alex Mikhalev 7 years ago
parent
commit
7846653297
  1. 7
      app/components/DeviceView.tsx
  2. 10
      app/components/ProgramTable.tsx
  3. 49
      app/components/SectionRunnerView.tsx
  4. 5
      app/components/SectionTable.tsx
  5. 14
      app/styles/app.css
  6. 16
      common/sprinklers/SectionRunner.ts
  7. 2
      common/sprinklers/schema/index.ts
  8. 11
      common/sprinklers/schema/requests.ts
  9. 2
      package.json
  10. 2
      server/index.ts
  11. 14
      yarn.lock

7
app/components/DeviceView.tsx

@ -1,8 +1,7 @@ @@ -1,8 +1,7 @@
import * as classNames from "classnames";
import { observer } from "mobx-react";
import * as React from "react";
import FontAwesome = require("react-fontawesome");
import { Header, Item } from "semantic-ui-react";
import { Header, Icon, Item } from "semantic-ui-react";
import { injectState, MqttApiState } from "@app/state";
import { SprinklersDevice } from "@common/sprinklers";
@ -16,7 +15,7 @@ const ConnectionState = ({ connected }: { connected: boolean }) => { @@ -16,7 +15,7 @@ const ConnectionState = ({ connected }: { connected: boolean }) => {
});
return (
<span className={classes}>
<FontAwesome name={connected ? "plug" : "chain-broken"} />&nbsp;
<Icon name={connected ? "linkify" : "unlinkify"} />&nbsp;
{connected ? "Connected" : "Disconnected"}
</span>
);
@ -51,10 +50,10 @@ class DeviceView extends React.Component<DeviceViewProps> { @@ -51,10 +50,10 @@ class DeviceView extends React.Component<DeviceViewProps> {
<Item.Meta>
Raspberry Pi Grinklers Instance
</Item.Meta>
<SectionRunnerView sectionRunner={sectionRunner} />
<SectionTable sections={sections} />
<RunSectionForm sections={sections} />
<ProgramTable programs={programs} />
<SectionRunnerView sectionRunner={sectionRunner} sections={sections} />
</Item.Content>
</Item>
);

10
app/components/ProgramTable.tsx

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
import { observer } from "mobx-react";
import * as React from "react";
import { Table } from "semantic-ui-react";
import { Button, Table } from "semantic-ui-react";
import { Duration } from "@common/Duration";
import { Program, Schedule } from "@common/sprinklers";
@ -27,11 +27,17 @@ export default class ProgramTable extends React.Component<{ programs: Program[] @@ -27,11 +27,17 @@ export default class ProgramTable extends React.Component<{ programs: Program[]
<li key={index}>Section {item.section + 1 + ""} for {duration.toString()}</li>
);
});
const cancelOrRun = () => running ? program.cancel() : program.run();
return [(
<Table.Row key={i}>
<Table.Cell className="program--number">{"" + (i + 1)}</Table.Cell>
<Table.Cell className="program--name">{name}</Table.Cell>
<Table.Cell className="program--running">{running ? "Running" : "Not running"}</Table.Cell>
<Table.Cell className="program--running">
{running ? "Running" : "Not running"}
<Button onClick={cancelOrRun}>
{running ? "Cancel" : "Run"}
</Button>
</Table.Cell>
<Table.Cell className="program--enabled">{enabled ? "Enabled" : "Not enabled"}</Table.Cell>
</Table.Row>
), (

49
app/components/SectionRunnerView.tsx

@ -1,16 +1,55 @@ @@ -1,16 +1,55 @@
import * as classNames from "classnames";
import { observer } from "mobx-react";
import * as React from "react";
import { Segment } from "semantic-ui-react";
import { Button, Icon, Segment } from "semantic-ui-react";
import { SectionRunner } from "@common/sprinklers";
import { Duration } from "@common/Duration";
import { Section, SectionRun, SectionRunner } from "@common/sprinklers";
function PausedState({ paused }: { paused: boolean }) {
const classes = classNames({
"sectionRunner--pausedState": true,
"sectionRunner--pausedState-paused": paused,
"sectionRunner--pausedState-unpaused": !paused,
});
return (
<span className={classes}>
<Icon name={paused ? "pause" : "play"} />
{paused ? "Paused" : "Processing"}
</span>
);
}
function SectionRunView({ run, sections }:
{ run: SectionRun, sections: Section[] }) {
const section = sections[run.section];
const current = run.startTime != null;
const duration = Duration.fromSeconds(run.duration);
const cancel = run.cancel;
return (
<Segment inverted={current} color={current ? "olive" : undefined}>
'{section.name}' for {duration.toString()}
<Button onClick={cancel} icon><Icon name="remove" /></Button>
</Segment>
);
}
@observer
export default class SectionRunnerView extends React.Component<{ sectionRunner: SectionRunner }, {}> {
export default class SectionRunnerView extends React.Component<{
sectionRunner: SectionRunner, sections: Section[],
}, {}> {
render() {
const { current, queue, paused } = this.props.sectionRunner;
const { sections } = this.props;
const queueView = queue.map((run) =>
<SectionRunView key={run.id} run={run} sections={sections} />);
return (
<Segment>
<h4>Section Runner Queue</h4>
{this.props.sectionRunner.toString()}
<h4>Section Runner Queue <PausedState paused={paused} /></h4>
<Segment.Group>
{current && <SectionRunView run={current} sections={sections} />}
{queueView}
</Segment.Group>
</Segment>
);
}

5
app/components/SectionTable.tsx

@ -1,8 +1,7 @@ @@ -1,8 +1,7 @@
import * as classNames from "classnames";
import { observer } from "mobx-react";
import * as React from "react";
import FontAwesome = require("react-fontawesome");
import { Table } from "semantic-ui-react";
import { Icon, Table } from "semantic-ui-react";
import { Section } from "@common/sprinklers";
@ -21,7 +20,7 @@ export default class SectionTable extends React.Component<{ sections: Section[] @@ -21,7 +20,7 @@ export default class SectionTable extends React.Component<{ sections: Section[]
"section--state-false": !state,
});
const sectionState = state ?
(<span><FontAwesome name="tint" /> Irrigating</span>)
(<span><Icon name="shower" /> Irrigating</span>)
: "Not irrigating";
return (
<Table.Row key={index}>

14
app/styles/app.css

@ -12,6 +12,20 @@ @@ -12,6 +12,20 @@
color: #D20000;
}
.sectionRunner--pausedState {
padding-left: .75em;
font-size: .75em;
font-weight: lighter;
}
.sectionRunner--pausedState > .fa {
padding-right: .2em;
}
.sectionRunner--pausedState-unpaused {
color: #BBBBBB;
}
.section--number,
.program--number {
width: 2em

16
common/sprinklers/SectionRunner.ts

@ -15,9 +15,7 @@ export class SectionRun { @@ -15,9 +15,7 @@ export class SectionRun {
this.section = section;
}
cancel() {
return this.sectionRunner.cancelRunById(this.id);
}
cancel = () => this.sectionRunner.cancelRunById(this.id);
toString() {
return `SectionRun{id=${this.id}, section=${this.section}, duration=${this.duration},` +
@ -40,6 +38,18 @@ export class SectionRunner { @@ -40,6 +38,18 @@ export class SectionRunner {
return this.device.cancelSectionRunId({ runId });
}
setPaused(paused: boolean) {
return this.device.pauseSectionRunner({ paused });
}
pause() {
return this.setPaused(true);
}
unpause() {
return this.setPaused(false);
}
toString(): string {
return `SectionRunner{queue="${this.queue}", current="${this.current}", paused=${this.paused}}`;
}

2
common/sprinklers/schema/index.ts

@ -21,7 +21,7 @@ export const section: ModelSchema<s.Section> = { @@ -21,7 +21,7 @@ export const section: ModelSchema<s.Section> = {
};
export const sectionRun: ModelSchema<s.SectionRun> = {
factory: (c) => new s.SectionRun(c.json.id),
factory: (c) => new s.SectionRun(c.parentContext.target, c.json.id),
props: {
id: primitive(),
section: primitive(),

11
common/sprinklers/schema/requests.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { createSimpleSchema, deserialize, ModelSchema, object, primitive, serialize } from "serializr";
import { createSimpleSchema, deserialize, ModelSchema, primitive, serialize } from "serializr";
import * as requests from "../requests";
import * as common from "./common";
@ -16,9 +16,12 @@ export const withSection: ModelSchema<requests.WithSection> = createSimpleSchema @@ -16,9 +16,12 @@ export const withSection: ModelSchema<requests.WithSection> = createSimpleSchema
sectionId: primitive(),
});
export const updateProgramData: ModelSchema<requests.UpdateProgramData> = createSimpleSchema({
export const updateProgram: ModelSchema<requests.UpdateProgramData> = createSimpleSchema({
...withProgram.props,
data: object(createSimpleSchema({ "*": true })),
data: {
serializer: (data) => data,
deserializer: (json, done) => { done(null, json); },
},
});
export const runSection: ModelSchema<requests.RunSectionData> = createSimpleSchema({
@ -42,7 +45,7 @@ export function getRequestSchema(request: requests.WithType): ModelSchema<any> { @@ -42,7 +45,7 @@ export function getRequestSchema(request: requests.WithType): ModelSchema<any> {
case "cancelProgram":
return withProgram;
case "updateProgram":
throw new Error("updateProgram not implemented");
return updateProgram;
case "runSection":
return runSection;
case "cancelSection":

2
package.json

@ -56,7 +56,6 @@ @@ -56,7 +56,6 @@
"@types/prop-types": "^15.5.1",
"@types/react": "^16.0.10",
"@types/react-dom": "^16.0.1",
"@types/react-fontawesome": "^1.5.0",
"@types/react-hot-loader": "^3.0.4",
"@types/webpack-env": "^1.13.0",
"@types/ws": "^3.2.0",
@ -85,7 +84,6 @@ @@ -85,7 +84,6 @@
"react": "^16.0.0",
"react-dev-utils": "^4.1.0",
"react-dom": "^16.0.0",
"react-fontawesome": "^1.6.1",
"react-hot-loader": "^3.0.0-beta.6",
"semantic-ui-css": "^2.2.10",
"semantic-ui-react": "^0.74.2",

2
server/index.ts

@ -54,7 +54,7 @@ async function deviceCallRequest(socket: WebSocket, data: ws.IDeviceCallRequest) @@ -54,7 +54,7 @@ async function deviceCallRequest(socket: WebSocket, data: ws.IDeviceCallRequest)
function webSocketHandler(socket: WebSocket) {
const stop = autorunAsync(() => {
const json = serialize(schema.sprinklersDevice, device);
log.info({ device: json });
log.trace({ device: json });
const data = { type: "deviceUpdate", name: "grinklers", data: json };
socket.send(JSON.stringify(data));
}, 100);

14
yarn.lock

@ -70,12 +70,6 @@ @@ -70,12 +70,6 @@
"@types/node" "*"
"@types/react" "*"
"@types/react-fontawesome@^1.5.0":
version "1.6.1"
resolved "https://registry.yarnpkg.com/@types/react-fontawesome/-/react-fontawesome-1.6.1.tgz#a07ff96f89c9a778cc7abb8e66b52f0c47bb3188"
dependencies:
"@types/react" "*"
"@types/react-hot-loader@^3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@types/react-hot-loader/-/react-hot-loader-3.0.4.tgz#7fc081509830c64218d8a99a865e2fb4a94572ad"
@ -4432,7 +4426,7 @@ promise@^8.0.1: @@ -4432,7 +4426,7 @@ promise@^8.0.1:
dependencies:
asap "~2.0.3"
prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.6.0:
prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.6.0:
version "15.6.0"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856"
dependencies:
@ -4619,12 +4613,6 @@ react-error-overlay@^2.0.2: @@ -4619,12 +4613,6 @@ react-error-overlay@^2.0.2:
settle-promise "1.0.0"
source-map "0.5.6"
react-fontawesome@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/react-fontawesome/-/react-fontawesome-1.6.1.tgz#eddce17e7dc731aa09fd4a186688a61793a16c5c"
dependencies:
prop-types "^15.5.6"
react-hot-loader@^3.0.0-beta.6:
version "3.0.0-beta.7"
resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-3.0.0-beta.7.tgz#d5847b8165d731c4d5b30d86d5d4716227a0fa83"

Loading…
Cancel
Save