Browse Source

Made things look better

update-deps
Alex Mikhalev 7 years ago
parent
commit
19d973ee71
  1. 24
      app/components/DeviceView.scss
  2. 13
      app/components/DeviceView.tsx
  3. 2
      app/components/DurationInput.tsx
  4. 98
      app/components/ProgramTable.tsx
  5. 2
      app/components/RunSectionForm.tsx
  6. 28
      app/styles/app.css
  7. 48
      app/webpack.config.js
  8. 7
      package.json
  9. 727
      yarn.lock

24
app/components/DeviceView.scss

@ -0,0 +1,24 @@
.device {
.header {
display: flex !important;
flex-direction: column;
@media only screen and (min-width : 768px) {
flex-direction: row;
}
}
.connectionState {
@media only screen and (min-width : 768px) {
margin-left: .75em;
}
font-size: .75em;
font-weight: lighter;
&.connected {
color: #13D213;
}
&.disconnected {
color: #D20000;
}
}
}

13
app/components/DeviceView.tsx

@ -6,12 +6,13 @@ import { Grid, Header, Icon, Item } from "semantic-ui-react";
import { injectState, StateBase } from "@app/state"; import { injectState, StateBase } from "@app/state";
import { SprinklersDevice } from "@common/sprinklers"; import { SprinklersDevice } from "@common/sprinklers";
import { ProgramTable, RunSectionForm, SectionRunnerView, SectionTable } from "."; import { ProgramTable, RunSectionForm, SectionRunnerView, SectionTable } from ".";
import "./DeviceView.scss";
function ConnectionState({ connected, className }: { connected: boolean, className?: string }) { function ConnectionState({ connected, className }: { connected: boolean, className?: string }) {
const classes = classNames({ const classes = classNames({
"device--connectionState": true, connectionState: true,
"device--connectionState-connected": connected, connected: connected, /* tslint:disable-line:object-literal-shorthand */
"device--connectionState-disconnected": !connected, disconnected: !connected,
}, className); }, className);
return ( return (
<div className={classes}> <div className={classes}>
@ -42,13 +43,13 @@ class DeviceView extends React.Component<DeviceViewProps> {
return ( return (
<Item> <Item>
<Item.Image src={require("@app/images/raspberry_pi.png")} /> <Item.Image src={require("@app/images/raspberry_pi.png")} />
<Item.Content> <Item.Content className="device">
<Header as="h1"> <Header as="h1">
<div>Device <kbd>{id}</kbd></div> <div>Device <kbd>{id}</kbd></div>
<ConnectionState connected={connected} /> <ConnectionState connected={connected} />
</Header> </Header>
<Item.Meta> <Item.Meta>
Raspberry Pi Grinklers Instance Raspberry Pi Grinklers Device
</Item.Meta> </Item.Meta>
<Grid> <Grid>
<Grid.Column mobile={16} largeScreen={8}> <Grid.Column mobile={16} largeScreen={8}>
@ -58,7 +59,7 @@ class DeviceView extends React.Component<DeviceViewProps> {
<RunSectionForm sections={sections} /> <RunSectionForm sections={sections} />
</Grid.Column> </Grid.Column>
</Grid> </Grid>
<ProgramTable programs={programs} /> <ProgramTable programs={programs} sections={sections} />
<SectionRunnerView sectionRunner={sectionRunner} sections={sections} /> <SectionRunnerView sectionRunner={sectionRunner} sections={sections} />
</Item.Content> </Item.Content>
</Item> </Item>

2
app/components/DurationInput.tsx

@ -16,7 +16,7 @@ export default class DurationInput extends React.Component<{
return ( return (
<div className={className}> <div className={className}>
<label>Duration</label> <label>Duration</label>
<div className="fields"> <div className="ui two fields">
<Input <Input
type="number" type="number"
className="field durationInput--minutes" className="field durationInput--minutes"

98
app/components/ProgramTable.tsx

@ -1,31 +1,81 @@
import { flatMap } from "lodash";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import * as moment from "moment";
import * as React from "react"; import * as React from "react";
import { Button, 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, TimeOfDay, Weekday, DateOfYear, Section } from "@common/sprinklers";
function timeToString(time: TimeOfDay) {
return moment(time).format("LTS");
}
function formatDateOfYear(day: DateOfYear | null, prefix: string) {
if (day == null) {
return null;
}
return prefix + moment(day).format("l");
}
@observer @observer
export class ScheduleView extends React.Component<{ schedule: Schedule }> { export class ScheduleView extends React.Component<{ schedule: Schedule }> {
render() { 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, "From ");
const to = formatDateOfYear(schedule.to, "To ");
return ( return (
<div>{JSON.stringify(this.props.schedule)}</div> <div>
At {times} <br />
On {weekdays} <br />
{from} <br />
{to}
</div>
); );
} }
} }
@observer @observer
export default class ProgramTable extends React.Component<{ programs: Program[] }> { export default class ProgramTable extends React.Component<{ programs: Program[], sections: Section[] }> {
private static renderRows(program: Program, i: number): JSX.Element[] | null { render() {
const programRows = Array.prototype.concat.apply([],
this.props.programs.map(this.renderRows));
return (
<Table celled>
<Table.Header>
<Table.Row>
<Table.HeaderCell colSpan="7">Programs</Table.HeaderCell>
</Table.Row>
<Table.Row>
<Table.HeaderCell className="program--number">#</Table.HeaderCell>
<Table.HeaderCell className="program--name">Name</Table.HeaderCell>
<Table.HeaderCell className="program--running">Running?</Table.HeaderCell>
<Table.HeaderCell className="program--enabled">Enabled?</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{programRows}
</Table.Body>
</Table>
);
}
private renderRows = (program: Program, i: number): JSX.Element[] | null => {
if (!program) { if (!program) {
return null; return null;
} }
const { name, running, enabled, schedule, sequence } = program; const { name, running, enabled, schedule, sequence } = program;
const sequenceItems = sequence.map((item, index) => { const sequenceItems = flatMap(sequence, (item, index) => {
const section = this.props.sections[item.section];
const duration = Duration.fromSeconds(item.duration); const duration = Duration.fromSeconds(item.duration);
return ( return [
<li key={index}>Section {item.section + 1 + ""} for {duration.toString()}</li> <em key={index}>"{section.name}"</em>, ` for ${duration.toString()}, `,
); ];
}); });
const cancelOrRun = () => running ? program.cancel() : program.run(); const cancelOrRun = () => running ? program.cancel() : program.run();
return [( return [(
@ -34,7 +84,7 @@ export default class ProgramTable extends React.Component<{ programs: Program[]
<Table.Cell className="program--name">{name}</Table.Cell> <Table.Cell className="program--name">{name}</Table.Cell>
<Table.Cell className="program--running"> <Table.Cell className="program--running">
{running ? "Running" : "Not running"} {running ? "Running" : "Not running"}
<Button onClick={cancelOrRun}> <Button className="program--runningButton" onClick={cancelOrRun}>
{running ? "Cancel" : "Run"} {running ? "Cancel" : "Run"}
</Button> </Button>
</Table.Cell> </Table.Cell>
@ -43,36 +93,10 @@ export default class ProgramTable extends React.Component<{ programs: Program[]
), ( ), (
<Table.Row key={i + .5}> <Table.Row key={i + .5}>
<Table.Cell className="program--sequence" colSpan="4"> <Table.Cell className="program--sequence" colSpan="4">
<ul> <h4>Sequence: </h4> {sequenceItems}
{sequenceItems} <h4>Schedule: </h4> <ScheduleView schedule={schedule} />
</ul>
<ScheduleView schedule={schedule} />
</Table.Cell> </Table.Cell>
</Table.Row> </Table.Row>
)]; )];
} }
render() {
const programRows = Array.prototype.concat.apply([],
this.props.programs.map(ProgramTable.renderRows));
return (
<Table celled>
<Table.Header>
<Table.Row>
<Table.HeaderCell colSpan="7">Programs</Table.HeaderCell>
</Table.Row>
<Table.Row>
<Table.HeaderCell className="program--number">#</Table.HeaderCell>
<Table.HeaderCell className="program--name">Name</Table.HeaderCell>
<Table.HeaderCell className="program--running">Running?</Table.HeaderCell>
<Table.HeaderCell className="program--enabled">Enabled?</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{programRows}
</Table.Body>
</Table>
);
}
} }

2
app/components/RunSectionForm.tsx

@ -29,7 +29,6 @@ export default class RunSectionForm extends React.Component<{
<Segment> <Segment>
<Header>Run Section</Header> <Header>Run Section</Header>
<Form> <Form>
<Form.Group className="doubling stackable three column ui grid">
<Form.Select <Form.Select
label="Section" label="Section"
placeholder="Section" placeholder="Section"
@ -50,7 +49,6 @@ export default class RunSectionForm extends React.Component<{
> >
Run Run
</Form.Button> </Form.Button>
</Form.Group>
</Form> </Form>
</Segment> </Segment>
); );

28
app/styles/app.css

@ -2,20 +2,6 @@
margin-top: 1em; margin-top: 1em;
} }
.device--connectionState {
margin-left: .75em;
font-size: .75em;
font-weight: lighter;
}
.device--connectionState-connected {
color: #13D213;
}
.device--connectionState-disconnected {
color: #D20000;
}
.sectionRunner--pausedState { .sectionRunner--pausedState {
padding-left: .75em; padding-left: .75em;
font-size: .75em; font-size: .75em;
@ -45,6 +31,10 @@
} }
.program--runningButton {
margin-left: 1em !important;
}
.section--state-true { .section--state-true {
color: green; color: green;
} }
@ -53,9 +43,13 @@
} }
.durationInput--minutes > input, .durationInput--minutes,
.durationInput--seconds > input { .durationInput--seconds {
width: 6em !important; min-width: 6em !important;
}
.durationInput .ui.labeled.input > .label {
width: 3em;
} }
.messages { .messages {

48
app/webpack.config.js

@ -1,4 +1,3 @@
const autoprefixer = require("autoprefixer");
const path = require("path"); const path = require("path");
const webpack = require("webpack"); const webpack = require("webpack");
@ -8,6 +7,7 @@ const CaseSensitivePathsPlugin = require("case-sensitive-paths-webpack-plugin");
const WatchMissingNodeModulesPlugin = require("react-dev-utils/WatchMissingNodeModulesPlugin"); const WatchMissingNodeModulesPlugin = require("react-dev-utils/WatchMissingNodeModulesPlugin");
// const UglifyJsPlugin = require("uglifyjs-webpack-plugin"); // const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const MinifyPlugin = require("babel-minify-webpack-plugin"); const MinifyPlugin = require("babel-minify-webpack-plugin");
const cssnext = require("postcss-cssnext");
const { getClientEnvironment } = require("../env"); const { getClientEnvironment } = require("../env");
const paths = require("../paths"); const paths = require("../paths");
@ -38,7 +38,7 @@ const postCssConfig = {
ident: "postcss", ident: "postcss",
plugins: () => [ plugins: () => [
require("postcss-flexbugs-fixes"), require("postcss-flexbugs-fixes"),
autoprefixer({ cssnext({
browsers: [ browsers: [
">1%", ">1%",
"last 4 versions", "last 4 versions",
@ -51,6 +51,11 @@ const postCssConfig = {
}, },
}; };
const sassConfig = {
loader: require.resolve("sass-loader"),
options: {},
};
const rules = (env) => { const rules = (env) => {
// "postcss" loader applies autoprefixer to our CSS. // "postcss" loader applies autoprefixer to our CSS.
// "css" loader resolves paths in CSS and adds assets as dependencies. // "css" loader resolves paths in CSS and adds assets as dependencies.
@ -95,6 +100,44 @@ const rules = (env) => {
publicPath, publicPath,
}) })
}; };
const sassRule = (env === "dev") ?
{
test: /\.scss$/,
use: [
require.resolve("style-loader"),
{
loader: require.resolve("css-loader"),
options: {
importLoaders: 1,
},
},
sassConfig,
],
} :
{
test: /\.css$/,
loader: ExtractTextPlugin.extract({
fallback: require.resolve('style-loader'),
use: [
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
minimize: true,
sourceMap: shouldUseSourceMap,
},
},
sassConfig
],
// ExtractTextPlugin expects the build output to be flat.
// (See https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/27)
// However, our output is structured with css, js and media folders.
// To have this structure working with relative paths, we have to use custom options.
publicPath: shouldUseRelativeAssetPaths ?
Array(cssFilename.split("/").length).join("../") :
publicPath,
})
};
return [ return [
{ {
test: /\.tsx?$/, test: /\.tsx?$/,
@ -119,6 +162,7 @@ const rules = (env) => {
}, },
}, },
cssRule, cssRule,
sassRule,
// Process TypeScript with TSC. // Process TypeScript with TSC.
{ {
test: /\.tsx?$/, use: { test: /\.tsx?$/, use: {

7
package.json

@ -21,7 +21,8 @@
"lint": "run-p lint:*" "lint": "run-p lint:*"
}, },
"files": [ "files": [
"dist", "public" "dist",
"public"
], ],
"repository": { "repository": {
"type": "git", "type": "git",
@ -38,8 +39,10 @@
"express-pino-logger": "^2.0.0", "express-pino-logger": "^2.0.0",
"mobx": "^3.1.11", "mobx": "^3.1.11",
"module-alias": "^2.0.1", "module-alias": "^2.0.1",
"moment": "^2.19.1",
"mqtt": "^2.13.0", "mqtt": "^2.13.0",
"pino": "^4.7.2", "pino": "^4.7.2",
"postcss-cssnext": "^3.0.2",
"serializr": "^1.1.13" "serializr": "^1.1.13"
}, },
"devDependencies": { "devDependencies": {
@ -74,6 +77,7 @@
"lodash": "^4.17.4", "lodash": "^4.17.4",
"mobx-react": "^4.2.1", "mobx-react": "^4.2.1",
"mobx-react-devtools": "^4.2.13", "mobx-react-devtools": "^4.2.13",
"node-sass": "^4.5.3",
"nodemon": "^1.12.1", "nodemon": "^1.12.1",
"npm-run-all": "^4.1.1", "npm-run-all": "^4.1.1",
"object-assign": "^4.1.1", "object-assign": "^4.1.1",
@ -85,6 +89,7 @@
"react-dev-utils": "^4.1.0", "react-dev-utils": "^4.1.0",
"react-dom": "^16.0.0", "react-dom": "^16.0.0",
"react-hot-loader": "^3.0.0-beta.6", "react-hot-loader": "^3.0.0-beta.6",
"sass-loader": "^6.0.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",
"source-map-loader": "^0.2.1", "source-map-loader": "^0.2.1",

727
yarn.lock

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save