Browse Source

Show next run time for programs

master
Alex Mikhalev 6 years ago
parent
commit
f6d6ef7c0c
  1. 6
      client/components/ProgramSequenceView.tsx
  2. 7
      client/components/ProgramTable.tsx
  3. 41
      client/pages/ProgramPage.tsx
  4. 5
      client/styles/DeviceView.scss
  5. 7
      common/sprinklersRpc/Program.ts
  6. 2
      common/sprinklersRpc/mqtt/MqttProgram.ts
  7. 5
      common/sprinklersRpc/schema/index.ts

6
client/components/ProgramSequenceView.tsx

@ -105,16 +105,17 @@ class ProgramSequenceItem extends React.Component<{ @@ -105,16 +105,17 @@ class ProgramSequenceItem extends React.Component<{
const ProgramSequenceItemD = SortableElement(ProgramSequenceItem);
// tslint:disable: no-shadowed-variable
const ProgramSequenceList = SortableContainer(
observer(
(props: {
function ProgramSequenceList(props: {
className: string;
list: ProgramItem[];
sections: Section[];
editing: boolean;
onChange: ItemChangeHandler;
onRemove: ItemRemoveHandler;
}) => {
}) {
const { className, list, sections, ...rest } = props;
const listItems = list.map((item, index) => {
const key = `item-${index}`;
@ -132,7 +133,6 @@ const ProgramSequenceList = SortableContainer( @@ -132,7 +133,6 @@ const ProgramSequenceList = SortableContainer(
return <ul className={className}>{listItems}</ul>;
}
),
{ withRef: true }
);
@observer

7
client/components/ProgramTable.tsx

@ -8,6 +8,7 @@ import { ProgramSequenceView, ScheduleView } from "@client/components"; @@ -8,6 +8,7 @@ import { ProgramSequenceView, ScheduleView } from "@client/components";
import * as route from "@client/routePaths";
import { ISprinklersDevice } from "@common/httpApi";
import { Program, SprinklersDevice } from "@common/sprinklersRpc";
import moment = require("moment");
@observer
class ProgramRows extends React.Component<{
@ -69,6 +70,12 @@ class ProgramRows extends React.Component<{ @@ -69,6 +70,12 @@ class ProgramRows extends React.Component<{
<h4>Sequence: </h4>{" "}
<ProgramSequenceView sequence={sequence} sections={sections} />
<ScheduleView schedule={schedule} label={<h4>Schedule: </h4>} />
<h4 className="program--nextRun">Next run: </h4>
{
program.nextRun
? <time title={moment(program.nextRun).toString()}>{moment(program.nextRun).fromNow()}</time>
: <time title="never">never</time>
}
</Form>
</Table.Cell>
</Table.Row>

41
client/pages/ProgramPage.tsx

@ -20,8 +20,9 @@ import { AppState, injectState } from "@client/state"; @@ -20,8 +20,9 @@ import { AppState, injectState } from "@client/state";
import { ISprinklersDevice } from "@common/httpApi";
import log from "@common/logger";
import { Program, SprinklersDevice } from "@common/sprinklersRpc";
import { action } from "mobx";
import classNames = require("classnames");
import { action } from "mobx";
import * as moment from "moment";
interface ProgramPageProps
extends RouteComponentProps<{ deviceId: string; programId: string }> {
@ -186,7 +187,6 @@ class ProgramPage extends React.Component<ProgramPageProps> { @@ -186,7 +187,6 @@ class ProgramPage extends React.Component<ProgramPageProps> {
toggle
label="Enabled"
checked={enabled}
readOnly={!editing}
onChange={this.onEnabledChange}
/>
<Form.Checkbox
@ -211,6 +211,16 @@ class ProgramPage extends React.Component<ProgramPageProps> { @@ -211,6 +211,16 @@ class ProgramPage extends React.Component<ProgramPageProps> {
editing={editing}
label={<h4>Schedule</h4>}
/>
{ !editing && (
<h4 className="program--nextRun">Next run: </h4>)
}
{
!editing && (
program.nextRun
? <time title={moment(program.nextRun).toString()}>{moment(program.nextRun).fromNow()}</time>
: <time title="never">never</time>
)
}
</Form>
</Modal.Content>
{this.renderActions(program)}
@ -243,6 +253,10 @@ class ProgramPage extends React.Component<ProgramPageProps> { @@ -243,6 +253,10 @@ class ProgramPage extends React.Component<ProgramPageProps> {
},
err => {
log.error({ err }, "error updating Program");
this.props.appState.uiStore.addMessage({
error: true,
content: `Error updating program: ${err}`,
});
}
);
this.stopEditing();
@ -268,11 +282,28 @@ class ProgramPage extends React.Component<ProgramPageProps> { @@ -268,11 +282,28 @@ class ProgramPage extends React.Component<ProgramPageProps> {
@action.bound
private onEnabledChange(e: any, p: CheckboxProps) {
if (this.programView) {
this.programView.enabled = p.checked!;
if (p.checked !== undefined && this.program) {
this.program.enabled = p.checked;
this.program.update().then(
data => {
log.info({ data }, "Program updated");
this.props.appState.uiStore.addMessage({
success: true,
content: `Program ${this.program!.name} ${this.program!.enabled ? "enabled" : "disabled"}`,
timeout: 2000,
});
},
err => {
log.error({ err }, "error updating Program");
this.props.appState.uiStore.addMessage({
error: true,
content: `Error updating program: ${err}`,
});
}
);
}
}
}
const DecoratedProgramPage = injectState(withRouter(observer(ProgramPage)));
const DecoratedProgramPage = injectState(withRouter(ProgramPage));
export default DecoratedProgramPage;

5
client/styles/DeviceView.scss

@ -78,6 +78,11 @@ $connected-color: #13d213; @@ -78,6 +78,11 @@ $connected-color: #13d213;
color: green;
}
.program--nextRun {
display: inline-block;
padding-right: 0.5em;
}
.ui.modal.programEditor {
&.editing > .content {
min-height: 80vh;

7
common/sprinklersRpc/Program.ts

@ -32,6 +32,8 @@ export class Program { @@ -32,6 +32,8 @@ export class Program {
sequence: ProgramItem[] = [];
@observable
running: boolean = false;
@observable
nextRun: Date | null = null;
constructor(device: SprinklersDevice, id: number, data?: Partial<Program>) {
this.device = device;
@ -60,7 +62,8 @@ export class Program { @@ -60,7 +62,8 @@ export class Program {
enabled: this.enabled,
running: this.running,
schedule: this.schedule.clone(),
sequence: this.sequence.slice()
sequence: this.sequence.slice(),
nextRun: this.nextRun,
});
}
@ -68,7 +71,7 @@ export class Program { @@ -68,7 +71,7 @@ export class Program {
return (
`Program{name="${this.name}", enabled=${this.enabled}, schedule=${
this.schedule
}, ` + `sequence=${this.sequence}, running=${this.running}}`
}, ` + `sequence=${this.sequence}, running=${this.running}, nextRun=${this.nextRun}}`
);
}
}

2
common/sprinklersRpc/mqtt/MqttProgram.ts

@ -7,6 +7,8 @@ export class MqttProgram extends s.Program { @@ -7,6 +7,8 @@ export class MqttProgram extends s.Program {
onMessage(payload: string, topic: string | undefined) {
if (topic === "running") {
this.running = payload === "true";
} else if (topic === "nextRun") {
this.nextRun = (payload.length > 0) ? new Date(Number(payload) * 1000.0) : null;
} else if (topic == null) {
this.updateFromJSON(JSON.parse(payload));
}

5
common/sprinklersRpc/schema/index.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { createSimpleSchema, ModelSchema, object, primitive } from "serializr";
import { createSimpleSchema, ModelSchema, object, primitive, date } from "serializr";
import * as s from "..";
import list from "./list";
@ -85,7 +85,8 @@ export const program: ModelSchema<s.Program> = { @@ -85,7 +85,8 @@ export const program: ModelSchema<s.Program> = {
enabled: primitive(),
schedule: object(schedule),
sequence: list(object(programItem)),
running: primitive()
running: primitive(),
nextRun: date(),
}
};

Loading…
Cancel
Save