Alex Mikhalev
7 years ago
12 changed files with 258 additions and 89 deletions
@ -0,0 +1,19 @@ |
|||||||
|
import * as React from "react"; |
||||||
|
|
||||||
|
import { Duration } from "@common/Duration"; |
||||||
|
import { ProgramItem, Section} from "@common/sprinklersRpc"; |
||||||
|
|
||||||
|
export default function ProgramSequenceView({ sequence, sections }: { |
||||||
|
sequence: ProgramItem[], sections: Section[], |
||||||
|
}) { |
||||||
|
const sequenceItems = sequence.map((item, index) => { |
||||||
|
const section = sections[item.section]; |
||||||
|
const duration = Duration.fromSeconds(item.duration); |
||||||
|
return ( |
||||||
|
<li key={index}> |
||||||
|
<em>"{section.name}"</em> for {duration.toString()} |
||||||
|
</li> |
||||||
|
); |
||||||
|
}); |
||||||
|
return <ul>{sequenceItems}</ul>; |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
import { observer } from "mobx-react"; |
||||||
|
import * as moment from "moment"; |
||||||
|
import * as React from "react"; |
||||||
|
|
||||||
|
import { DateOfYear, Schedule, TimeOfDay, Weekday } from "@common/sprinklersRpc"; |
||||||
|
|
||||||
|
function timeToString(time: TimeOfDay) { |
||||||
|
return moment(time).format("LTS"); |
||||||
|
} |
||||||
|
|
||||||
|
function formatDateOfYear(day: DateOfYear | null, prefix: React.ReactNode) { |
||||||
|
if (day == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
const format = (day.year === 0) ? "M/D" : "l"; |
||||||
|
return <React.Fragment>{prefix}{moment(day).format(format)}</React.Fragment>; |
||||||
|
} |
||||||
|
|
||||||
|
@observer |
||||||
|
export default class ScheduleView extends React.Component<{ schedule: Schedule }> { |
||||||
|
render() { |
||||||
|
const { schedule } = this.props; |
||||||
|
const times = schedule.times.map((time, i) => timeToString(time)) |
||||||
|
.join(", "); |
||||||
|
const weekdays = schedule.weekdays.map((weekday) => |
||||||
|
Weekday[weekday]).join(", "); |
||||||
|
const from = formatDateOfYear(schedule.from, <b>From </b>); |
||||||
|
const to = formatDateOfYear(schedule.to, <b>To </b>); |
||||||
|
return ( |
||||||
|
<div> |
||||||
|
<b>At</b> {times} <br/> |
||||||
|
<b>On</b> {weekdays} <br/> |
||||||
|
{from} <br/> |
||||||
|
{to} |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,69 @@ |
|||||||
|
import { observer } from "mobx-react"; |
||||||
|
import * as React from "react"; |
||||||
|
import { Button, Menu, Segment} from "semantic-ui-react"; |
||||||
|
|
||||||
|
import { ProgramSequenceView, ScheduleView } from "@app/components"; |
||||||
|
import { AppState, injectState } from "@app/state"; |
||||||
|
import { Program, SprinklersDevice } from "@common/sprinklersRpc"; |
||||||
|
import { RouteComponentProps } from "react-router"; |
||||||
|
import { Link } from "react-router-dom"; |
||||||
|
|
||||||
|
@observer |
||||||
|
class ProgramDetailView extends React.Component { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@observer |
||||||
|
class ProgramPage extends React.Component<{ |
||||||
|
appState: AppState, |
||||||
|
} & RouteComponentProps<{ deviceId: string, programId: number }>> { |
||||||
|
device!: SprinklersDevice; |
||||||
|
|
||||||
|
render() { |
||||||
|
const { deviceId, programId } = this.props.match.params; |
||||||
|
const device = this.device = this.props.appState.sprinklersRpc.getDevice(deviceId); |
||||||
|
// TODO: check programId
|
||||||
|
if (device.programs.length <= programId || !device.programs[programId]) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
const program = device.programs[programId]; |
||||||
|
|
||||||
|
const programRows = this.renderRows(program, programId); |
||||||
|
return ( |
||||||
|
<div> |
||||||
|
<Menu attached="top"> |
||||||
|
<Menu.Item header>Program {program.name} ({program.id})</Menu.Item> |
||||||
|
<Menu.Menu position="right"> |
||||||
|
<Menu.Item> |
||||||
|
<Button as={Link} to={"/devices/" + deviceId}> |
||||||
|
Back |
||||||
|
</Button> |
||||||
|
</Menu.Item> |
||||||
|
</Menu.Menu> |
||||||
|
</Menu> |
||||||
|
<Segment attached="bottom"> |
||||||
|
{programRows} |
||||||
|
</Segment> |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
private renderRows = (program: Program, i: number): JSX.Element | null => { |
||||||
|
const { name, running, enabled, schedule, sequence } = program; |
||||||
|
const cancelOrRun = () => running ? program.cancel() : program.run(); |
||||||
|
return ( |
||||||
|
<React.Fragment key={i}> |
||||||
|
<b>Enabled: </b>{enabled ? "Enabled" : "Not enabled"}<br/> |
||||||
|
<b>Running: </b>{running ? "Running" : "Not running"}<br/> |
||||||
|
<Button size="small" onClick={cancelOrRun}> |
||||||
|
{running ? "Cancel" : "Run"} |
||||||
|
</Button> |
||||||
|
<h4>Sequence: </h4> <ProgramSequenceView sequence={sequence} sections={this.device.sections}/> |
||||||
|
<h4>Schedule: </h4> <ScheduleView schedule={schedule}/> |
||||||
|
</React.Fragment> |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const DecoratedProgramPage = injectState(observer(ProgramPage)); |
||||||
|
export default DecoratedProgramPage; |
@ -0,0 +1,23 @@ |
|||||||
|
export interface RouteParams { |
||||||
|
deviceId: string; |
||||||
|
programId: string; |
||||||
|
} |
||||||
|
|
||||||
|
export const routerRouteParams: RouteParams = { |
||||||
|
deviceId: ":deviceId", |
||||||
|
programId: ":programId", |
||||||
|
}; |
||||||
|
|
||||||
|
export const home = "/"; |
||||||
|
export const messagesTest = "/messagesTest"; |
||||||
|
|
||||||
|
export const login = "/login"; |
||||||
|
export const logout = "/logout"; |
||||||
|
|
||||||
|
export function device(deviceId?: string | number): string { |
||||||
|
return `/devices/${deviceId || ""}`; |
||||||
|
} |
||||||
|
|
||||||
|
export function program(deviceId: string | number, programId?: string | number): string { |
||||||
|
return `${device(deviceId)}/programs/${programId}`; |
||||||
|
} |
Loading…
Reference in new issue