Started UI improvements
This commit is contained in:
parent
3ce9bb4116
commit
7846653297
@ -1,8 +1,7 @@
|
|||||||
import * as classNames from "classnames";
|
import * as classNames from "classnames";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import FontAwesome = require("react-fontawesome");
|
import { Header, Icon, Item } from "semantic-ui-react";
|
||||||
import { Header, Item } from "semantic-ui-react";
|
|
||||||
|
|
||||||
import { injectState, MqttApiState } from "@app/state";
|
import { injectState, MqttApiState } from "@app/state";
|
||||||
import { SprinklersDevice } from "@common/sprinklers";
|
import { SprinklersDevice } from "@common/sprinklers";
|
||||||
@ -16,7 +15,7 @@ const ConnectionState = ({ connected }: { connected: boolean }) => {
|
|||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<span className={classes}>
|
<span className={classes}>
|
||||||
<FontAwesome name={connected ? "plug" : "chain-broken"} />
|
<Icon name={connected ? "linkify" : "unlinkify"} />
|
||||||
{connected ? "Connected" : "Disconnected"}
|
{connected ? "Connected" : "Disconnected"}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
@ -51,10 +50,10 @@ class DeviceView extends React.Component<DeviceViewProps> {
|
|||||||
<Item.Meta>
|
<Item.Meta>
|
||||||
Raspberry Pi Grinklers Instance
|
Raspberry Pi Grinklers Instance
|
||||||
</Item.Meta>
|
</Item.Meta>
|
||||||
<SectionRunnerView sectionRunner={sectionRunner} />
|
|
||||||
<SectionTable sections={sections} />
|
<SectionTable sections={sections} />
|
||||||
<RunSectionForm sections={sections} />
|
<RunSectionForm sections={sections} />
|
||||||
<ProgramTable programs={programs} />
|
<ProgramTable programs={programs} />
|
||||||
|
<SectionRunnerView sectionRunner={sectionRunner} sections={sections} />
|
||||||
</Item.Content>
|
</Item.Content>
|
||||||
</Item>
|
</Item>
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import * as React from "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 { Duration } from "@common/Duration";
|
||||||
import { Program, Schedule } from "@common/sprinklers";
|
import { Program, Schedule } from "@common/sprinklers";
|
||||||
@ -27,11 +27,17 @@ export default class ProgramTable extends React.Component<{ programs: Program[]
|
|||||||
<li key={index}>Section {item.section + 1 + ""} for {duration.toString()}</li>
|
<li key={index}>Section {item.section + 1 + ""} for {duration.toString()}</li>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
const cancelOrRun = () => running ? program.cancel() : program.run();
|
||||||
return [(
|
return [(
|
||||||
<Table.Row key={i}>
|
<Table.Row key={i}>
|
||||||
<Table.Cell className="program--number">{"" + (i + 1)}</Table.Cell>
|
<Table.Cell className="program--number">{"" + (i + 1)}</Table.Cell>
|
||||||
<Table.Cell className="program--name">{name}</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.Cell className="program--enabled">{enabled ? "Enabled" : "Not enabled"}</Table.Cell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
), (
|
), (
|
||||||
|
@ -1,16 +1,55 @@
|
|||||||
|
import * as classNames from "classnames";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import * as React from "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
|
@observer
|
||||||
export default class SectionRunnerView extends React.Component<{ sectionRunner: SectionRunner }, {}> {
|
export default class SectionRunnerView extends React.Component<{
|
||||||
|
sectionRunner: SectionRunner, sections: Section[],
|
||||||
|
}, {}> {
|
||||||
render() {
|
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 (
|
return (
|
||||||
<Segment>
|
<Segment>
|
||||||
<h4>Section Runner Queue</h4>
|
<h4>Section Runner Queue <PausedState paused={paused} /></h4>
|
||||||
{this.props.sectionRunner.toString()}
|
<Segment.Group>
|
||||||
|
{current && <SectionRunView run={current} sections={sections} />}
|
||||||
|
{queueView}
|
||||||
|
</Segment.Group>
|
||||||
</Segment>
|
</Segment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import * as classNames from "classnames";
|
import * as classNames from "classnames";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import FontAwesome = require("react-fontawesome");
|
import { Icon, Table } from "semantic-ui-react";
|
||||||
import { Table } from "semantic-ui-react";
|
|
||||||
|
|
||||||
import { Section } from "@common/sprinklers";
|
import { Section } from "@common/sprinklers";
|
||||||
|
|
||||||
@ -21,7 +20,7 @@ export default class SectionTable extends React.Component<{ sections: Section[]
|
|||||||
"section--state-false": !state,
|
"section--state-false": !state,
|
||||||
});
|
});
|
||||||
const sectionState = state ?
|
const sectionState = state ?
|
||||||
(<span><FontAwesome name="tint" /> Irrigating</span>)
|
(<span><Icon name="shower" /> Irrigating</span>)
|
||||||
: "Not irrigating";
|
: "Not irrigating";
|
||||||
return (
|
return (
|
||||||
<Table.Row key={index}>
|
<Table.Row key={index}>
|
||||||
|
@ -12,6 +12,20 @@
|
|||||||
color: #D20000;
|
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,
|
.section--number,
|
||||||
.program--number {
|
.program--number {
|
||||||
width: 2em
|
width: 2em
|
||||||
|
@ -15,9 +15,7 @@ export class SectionRun {
|
|||||||
this.section = section;
|
this.section = section;
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel() {
|
cancel = () => this.sectionRunner.cancelRunById(this.id);
|
||||||
return this.sectionRunner.cancelRunById(this.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
return `SectionRun{id=${this.id}, section=${this.section}, duration=${this.duration},` +
|
return `SectionRun{id=${this.id}, section=${this.section}, duration=${this.duration},` +
|
||||||
@ -40,6 +38,18 @@ export class SectionRunner {
|
|||||||
return this.device.cancelSectionRunId({ runId });
|
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 {
|
toString(): string {
|
||||||
return `SectionRunner{queue="${this.queue}", current="${this.current}", paused=${this.paused}}`;
|
return `SectionRunner{queue="${this.queue}", current="${this.current}", paused=${this.paused}}`;
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ export const section: ModelSchema<s.Section> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const sectionRun: ModelSchema<s.SectionRun> = {
|
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: {
|
props: {
|
||||||
id: primitive(),
|
id: primitive(),
|
||||||
section: primitive(),
|
section: primitive(),
|
||||||
|
@ -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 requests from "../requests";
|
||||||
import * as common from "./common";
|
import * as common from "./common";
|
||||||
|
|
||||||
@ -16,9 +16,12 @@ export const withSection: ModelSchema<requests.WithSection> = createSimpleSchema
|
|||||||
sectionId: primitive(),
|
sectionId: primitive(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const updateProgramData: ModelSchema<requests.UpdateProgramData> = createSimpleSchema({
|
export const updateProgram: ModelSchema<requests.UpdateProgramData> = createSimpleSchema({
|
||||||
...withProgram.props,
|
...withProgram.props,
|
||||||
data: object(createSimpleSchema({ "*": true })),
|
data: {
|
||||||
|
serializer: (data) => data,
|
||||||
|
deserializer: (json, done) => { done(null, json); },
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const runSection: ModelSchema<requests.RunSectionData> = createSimpleSchema({
|
export const runSection: ModelSchema<requests.RunSectionData> = createSimpleSchema({
|
||||||
@ -42,7 +45,7 @@ export function getRequestSchema(request: requests.WithType): ModelSchema<any> {
|
|||||||
case "cancelProgram":
|
case "cancelProgram":
|
||||||
return withProgram;
|
return withProgram;
|
||||||
case "updateProgram":
|
case "updateProgram":
|
||||||
throw new Error("updateProgram not implemented");
|
return updateProgram;
|
||||||
case "runSection":
|
case "runSection":
|
||||||
return runSection;
|
return runSection;
|
||||||
case "cancelSection":
|
case "cancelSection":
|
||||||
|
@ -56,7 +56,6 @@
|
|||||||
"@types/prop-types": "^15.5.1",
|
"@types/prop-types": "^15.5.1",
|
||||||
"@types/react": "^16.0.10",
|
"@types/react": "^16.0.10",
|
||||||
"@types/react-dom": "^16.0.1",
|
"@types/react-dom": "^16.0.1",
|
||||||
"@types/react-fontawesome": "^1.5.0",
|
|
||||||
"@types/react-hot-loader": "^3.0.4",
|
"@types/react-hot-loader": "^3.0.4",
|
||||||
"@types/webpack-env": "^1.13.0",
|
"@types/webpack-env": "^1.13.0",
|
||||||
"@types/ws": "^3.2.0",
|
"@types/ws": "^3.2.0",
|
||||||
@ -85,7 +84,6 @@
|
|||||||
"react": "^16.0.0",
|
"react": "^16.0.0",
|
||||||
"react-dev-utils": "^4.1.0",
|
"react-dev-utils": "^4.1.0",
|
||||||
"react-dom": "^16.0.0",
|
"react-dom": "^16.0.0",
|
||||||
"react-fontawesome": "^1.6.1",
|
|
||||||
"react-hot-loader": "^3.0.0-beta.6",
|
"react-hot-loader": "^3.0.0-beta.6",
|
||||||
"semantic-ui-css": "^2.2.10",
|
"semantic-ui-css": "^2.2.10",
|
||||||
"semantic-ui-react": "^0.74.2",
|
"semantic-ui-react": "^0.74.2",
|
||||||
|
@ -54,7 +54,7 @@ async function deviceCallRequest(socket: WebSocket, data: ws.IDeviceCallRequest)
|
|||||||
function webSocketHandler(socket: WebSocket) {
|
function webSocketHandler(socket: WebSocket) {
|
||||||
const stop = autorunAsync(() => {
|
const stop = autorunAsync(() => {
|
||||||
const json = serialize(schema.sprinklersDevice, device);
|
const json = serialize(schema.sprinklersDevice, device);
|
||||||
log.info({ device: json });
|
log.trace({ device: json });
|
||||||
const data = { type: "deviceUpdate", name: "grinklers", data: json };
|
const data = { type: "deviceUpdate", name: "grinklers", data: json };
|
||||||
socket.send(JSON.stringify(data));
|
socket.send(JSON.stringify(data));
|
||||||
}, 100);
|
}, 100);
|
||||||
|
14
yarn.lock
14
yarn.lock
@ -70,12 +70,6 @@
|
|||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
"@types/react" "*"
|
"@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":
|
"@types/react-hot-loader@^3.0.4":
|
||||||
version "3.0.4"
|
version "3.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react-hot-loader/-/react-hot-loader-3.0.4.tgz#7fc081509830c64218d8a99a865e2fb4a94572ad"
|
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:
|
|||||||
dependencies:
|
dependencies:
|
||||||
asap "~2.0.3"
|
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"
|
version "15.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856"
|
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -4619,12 +4613,6 @@ react-error-overlay@^2.0.2:
|
|||||||
settle-promise "1.0.0"
|
settle-promise "1.0.0"
|
||||||
source-map "0.5.6"
|
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:
|
react-hot-loader@^3.0.0-beta.6:
|
||||||
version "3.0.0-beta.7"
|
version "3.0.0-beta.7"
|
||||||
resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-3.0.0-beta.7.tgz#d5847b8165d731c4d5b30d86d5d4716227a0fa83"
|
resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-3.0.0-beta.7.tgz#d5847b8165d731c4d5b30d86d5d4716227a0fa83"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user