cleanup and fixes

pull/1395/head
gabrielburnworth 2019-08-26 11:20:46 -07:00
parent 75b72e13da
commit 3a20d17eaf
27 changed files with 131 additions and 105 deletions

View File

@ -126,6 +126,8 @@ export class API {
/** Rather than returning ALL logs, returns a filtered subset.
* /api/logs/search */
get filteredLogsPath() { return `${this.baseUrl}/api/logs/search`; }
/** /api/logs/ */
get logsPath() { return `${this.baseUrl}/api/logs/`; }
/** /api/webcam_feed */
get webcamFeedPath() { return `${this.baseUrl}/api/webcam_feeds/`; }
/** /api/web_app_config */

View File

@ -261,7 +261,7 @@ export function urlFor(tag: ResourceName) {
FbosConfig: API.current.fbosConfigPath,
FirmwareConfig: API.current.firmwareConfigPath,
Image: API.current.imagesPath,
Log: API.current.filteredLogsPath,
Log: API.current.logsPath,
Peripheral: API.current.peripheralsPath,
PinBinding: API.current.pinBindingPath,
PlantTemplate: API.current.plantTemplatePath,

View File

@ -1,6 +1,7 @@
import * as React from "react";
import { Row, Col } from "../ui/index";
import { KeyValRowProps } from "./key_val_show_row";
import { t } from "../i18next_wrapper";
interface Props extends KeyValRowProps {
onLabelChange(e: React.ChangeEvent<HTMLInputElement>): void;
@ -26,7 +27,8 @@ export function KeyValEditRow(p: Props) {
</Col>
<Col xs={2}>
<button
className="red fb-button"
className="red fb-button del-button"
title={t("Delete")}
onClick={p.onClick}>
<i className="fa fa-times" />
</button>

View File

@ -10,10 +10,10 @@ import { TaggedPeripheral, TaggedSensor } from "farmbot";
import { UUID } from "../resources/interfaces";
import { isNumber } from "lodash";
const MODES: { [s: string]: string } = {
const MODES = (): { [s: string]: string } => ({
0: t("Digital"),
1: t("Analog")
};
});
interface NameInputBoxProps {
dispatch: Function;
@ -53,11 +53,11 @@ interface ModeDropdownProps {
export const ModeDropdown = (props: ModeDropdownProps) =>
<FBSelect
onChange={d => {
props.dispatch(edit(props.resource, { mode: parseInt(d.value.toString(), 10) }));
}}
selectedItem={{ label: MODES[props.value], value: props.value }}
list={PIN_MODES} />;
onChange={d => props.dispatch(edit(props.resource, {
mode: parseInt(d.value.toString(), 10)
}))}
selectedItem={{ label: MODES()[props.value], value: props.value }}
list={PIN_MODES()} />;
interface DeleteButtonProps {
dispatch: Function;
@ -66,15 +66,12 @@ interface DeleteButtonProps {
onDestroy?: Function;
}
const DELETE_BTN = <i className="fa fa-times" />;
export const DeleteButton = (props: DeleteButtonProps) =>
<button
className="red fb-button"
onClick={() => {
props
.dispatch(destroy(props.uuid))
.then(props.onDestroy || (() => { }));
}}>
{props.children ? props.children : DELETE_BTN}
className="red fb-button del-button"
title={t("Delete")}
onClick={() =>
props.dispatch(destroy(props.uuid))
.then(props.onDestroy || (() => { }))}>
{props.children || <i className="fa fa-times" />}
</button >;

View File

@ -238,7 +238,7 @@
display: inline-block;
vertical-align: middle;
white-space: nowrap;
width: 8em;
width: 40%;
overflow: hidden;
text-overflow: ellipsis;
margin-left: 1rem;

View File

@ -469,9 +469,3 @@
min-width: 7rem;
}
}
.point-panel-content {
.point-search-item-name {
width: 40%;
}
}

View File

@ -396,7 +396,9 @@ a {
margin-left: 0.5rem;
}
.fb-button {
margin-top: 0.5rem;
&.green {
margin-top: 0.5rem;
}
}
.bindings-list {
margin-bottom: 1rem;
@ -404,7 +406,7 @@ a {
}
.stock-pin-bindings-button {
button {
margin: 0;
margin: 0 !important;
}
i {
margin-right: 0.5rem;
@ -1012,10 +1014,13 @@ ul {
.logs-table {
background: $white;
div {
.log-verbosity-saucer, .saucer {
float: left;
margin-right: 10px;
}
button {
float: none;
}
thead {
background: $gray;
}
@ -1096,9 +1101,6 @@ ul {
color: $dark_gray;
margin-top: 0.5rem;
}
.del-button {
margin-top: 0.5rem;
}
.fb-button {
margin-left: 1rem;
}
@ -1153,6 +1155,16 @@ ul {
}
}
.sensors-widget,
.webcam-widget,
.peripherals-widget,
.tools-widget,
.toolbay-widget {
.del-button {
margin-top: 0.5rem;
}
}
.farmware-step-input-fields {
label {
padding-top: 1rem;

View File

@ -25,6 +25,7 @@ export class DiagnosticDumpRow extends React.Component<Props, {}> {
<Col xs={1}>
<button
className="red fb-button del-button"
title={t("Delete")}
onClick={this.destroy}>
<i className="fa fa-times" />
</button>

View File

@ -114,7 +114,7 @@ export const colorFromThrottle =
const THROTTLE_COLOR_KEY = () => ({
red: t("active"),
yellow: t("occurred"),
green: t("clear")
green: t("ok")
});
interface ThrottleIndicatorProps {

View File

@ -45,7 +45,8 @@ export const PinBindingsList = (props: PinBindingsListProps) => {
</Col>
<Col xs={PinBindingColWidth.button}>
<button
className={`fb-button ${delBtnColor(pin_number)}`}
className={`fb-button ${delBtnColor(pin_number)} del-button`}
title={t("Delete")}
onClick={() => deleteBinding(pin_number, x.uuid)}>
<i className="fa fa-times" />
</button>

View File

@ -34,7 +34,7 @@ export class ImageFilterMenu
this.state = {};
}
componentWillMount() {
UNSAFE_componentWillMount() {
const { newestDate, toOldest } = this.props.imageAgeInfo;
const beginDatetime = this.props.getConfigValue("photo_filter_begin");
this.setState({

View File

@ -73,7 +73,7 @@ export class SpreadCircle extends
React.Component<SpreadCircleProps, SpreadCircleState> {
state: SpreadCircleState = { spread: undefined };
componentWillMount = () => {
UNSAFE_componentWillMount = () => {
cachedCrop(this.props.plant.body.openfarm_slug)
.then(({ spread }) => this.setState({ spread }));
}

View File

@ -1,5 +1,4 @@
import * as React from "react";
import { Row, Col, BlurableInput } from "../../ui";
import { error } from "../../toast/toast";
import { isNumber, isString } from "lodash";
@ -62,7 +61,8 @@ const ApplyGardenButton =
const DestroyGardenButton =
(props: { dispatch: Function, gardenUuid: string }) =>
<button
className="fb-button red"
className="fb-button red del-button"
title={t("Delete")}
onClick={() => props.dispatch(destroySavedGarden(props.gardenUuid))}>
<i className="fa fa-times" />
</button>;

View File

@ -123,7 +123,7 @@ export class FarmwarePage extends React.Component<FarmwareProps, {}> {
return isBotOnline(this.props.syncStatus, this.props.botToMqttStatus);
}
componentWillMount() {
UNSAFE_componentWillMount() {
if (window.innerWidth > 450) {
this.props.dispatch({
type: Actions.SELECT_FARMWARE,

View File

@ -39,8 +39,8 @@ export class HotKeys extends React.Component<Props, Partial<State>> {
onClose={this.toggle("guideOpen")}>
<div className={hotkeyGuideClasses}>
<h3>{t("Hotkeys")}</h3>
<i
className="fa fa-times"
<i className="fa fa-times"
title={t("Close")}
onClick={this.toggle("guideOpen")} />
{this.hotkeys(this.props.dispatch, "")
.map(hotkey => <Row key={hotkey.combo}>

View File

@ -1,14 +1,16 @@
import * as React from "react";
import { TaggedLog } from "farmbot";
import { TaggedLog, ALLOWED_MESSAGE_TYPES } from "farmbot";
import { LogsState, LogsTableProps, Filters } from "../interfaces";
import { formatLogTime } from "../index";
import { Classes } from "@blueprintjs/core";
import { isNumber, startCase } from "lodash";
import { t } from "../../i18next_wrapper";
import { TimeSettings } from "../../interfaces";
import { UUID } from "../../resources/interfaces";
interface LogsRowProps {
tlog: TaggedLog;
dispatch: Function;
timeSettings: TimeSettings;
}
@ -18,18 +20,31 @@ export const xyzTableEntry =
? `${x}, ${y}, ${z}`
: t("Unknown");
interface LogVerbositySaucerProps {
uuid: UUID;
verbosity: number | undefined;
type: ALLOWED_MESSAGE_TYPES;
dispatch: Function;
}
const LogVerbositySaucer = (props: LogVerbositySaucerProps) =>
<div className="log-verbosity-saucer">
<div className={`saucer ${props.type}`}>
<p>
{props.verbosity}
</p>
</div>
</div>;
/** A log is displayed in a single row of the logs table. */
const LogsRow = ({ tlog, timeSettings }: LogsRowProps) => {
const LogsRow = ({ tlog, timeSettings, dispatch }: LogsRowProps) => {
const { uuid } = tlog;
const { x, y, z, verbosity, type, created_at, message } = tlog.body;
const { x, y, z, verbosity, type, created_at, message, id } = tlog.body;
const time = formatLogTime(created_at || NaN, timeSettings);
return <tr key={uuid}>
return <tr key={uuid} id={"" + id}>
<td>
<div className={`saucer ${type}`}>
<p>
{verbosity}
</p>
</div>
<LogVerbositySaucer
uuid={uuid} dispatch={dispatch} verbosity={verbosity} type={type} />
{t(startCase(type))}
</td>
<td>
@ -63,12 +78,12 @@ export const LogsTable = (props: LogsTableProps) => {
</thead>
<tbody>
{filterByVerbosity(getFilterLevel(props.state), props.logs)
.map((log: TaggedLog) => {
return <LogsRow
.map((log: TaggedLog) =>
<LogsRow
key={log.uuid}
tlog={log}
timeSettings={props.timeSettings} />;
})}
dispatch={props.dispatch}
timeSettings={props.timeSettings} />)}
</tbody>
</table>;
};

View File

@ -118,6 +118,7 @@ export class Logs extends React.Component<LogsProps, Partial<LogsState>> {
</Row>
<Row>
<LogsTable logs={this.props.logs}
dispatch={this.props.dispatch}
state={this.state}
timeSettings={this.props.timeSettings} />
</Row>

View File

@ -20,6 +20,7 @@ export interface LogsState extends Filters {
export interface LogsTableProps {
logs: TaggedLog[];
dispatch: Function;
state: LogsState;
timeSettings: TimeSettings;
}

View File

@ -29,7 +29,7 @@ export const RegimenBackButton = (props: RegimenBackButtonProps) => {
@connect(mapStateToProps)
export class Regimens extends React.Component<Props, {}> {
componentWillMount() {
UNSAFE_componentWillMount() {
if (!this.props.current) { setActiveRegimenByName(); }
}

View File

@ -14,50 +14,50 @@ import { Provider } from "react-redux";
interface RootComponentProps { store: Store; }
export const attachAppToDom = () => {
attachToRoot(RootComponent, { store: _store });
// tslint:disable-next-line:no-any
_store.dispatch(ready() as any);
attachToRoot(RootComponent, { store: _store });
// tslint:disable-next-line:no-any
_store.dispatch(ready() as any);
};
interface RootComponentState {
Route: React.ComponentType;
ChildRoute?: React.ComponentType;
Route: React.ComponentType;
ChildRoute?: React.ComponentType;
}
export class RootComponent extends React.Component<RootComponentProps, RootComponentState> {
state: RootComponentState = { Route: () => <div>Loading...</div> };
state: RootComponentState = { Route: () => <div>Loading...</div> };
componentWillMount() {
const notLoggedIn = !Session.fetchStoredToken();
const currentLocation = history.getCurrentLocation().pathname;
const restrictedArea = currentLocation.includes("/app");
(notLoggedIn && restrictedArea && Session.clear());
}
changeRoute =
(Route: React.ComponentType, ChildRoute?: React.ComponentType) => {
this.setState({ Route: Route, ChildRoute });
};
componentDidMount() {
const main_routes = UNBOUND_ROUTES.map(bindTo => bindTo(this.changeRoute));
new Router(main_routes).enableHtml5Routing("/app").init();
}
render() {
const { Route } = this.state;
const { ChildRoute } = this.state;
const props = ChildRoute ? { children: <ChildRoute /> } : {};
try {
return <ErrorBoundary>
<Provider store={_store}>
<App {...{} as App["props"]}>
<Route {...props} />
</App>
</Provider>
</ErrorBoundary>;
} catch (error) {
return <p> Problem loading page.</p>;
}
UNSAFE_componentWillMount() {
const notLoggedIn = !Session.fetchStoredToken();
const currentLocation = history.getCurrentLocation().pathname;
const restrictedArea = currentLocation.includes("/app");
(notLoggedIn && restrictedArea && Session.clear());
}
changeRoute =
(Route: React.ComponentType, ChildRoute?: React.ComponentType) => {
this.setState({ Route: Route, ChildRoute });
};
componentDidMount() {
const main_routes = UNBOUND_ROUTES.map(bindTo => bindTo(this.changeRoute));
new Router(main_routes).enableHtml5Routing("/app").init();
}
render() {
const { Route } = this.state;
const { ChildRoute } = this.state;
const props = ChildRoute ? { children: <ChildRoute /> } : {};
try {
return <ErrorBoundary>
<Provider store={_store}>
<App {...{} as App["props"]}>
<Route {...props} />
</App>
</Provider>
</ErrorBoundary>;
} catch (error) {
return <p> Problem loading page.</p>;
}
}
}

View File

@ -31,7 +31,7 @@ export const SequenceBackButton = (props: SequenceBackButtonProps) => {
@connect(mapStateToProps)
export class Sequences extends React.Component<Props, {}> {
componentWillMount() {
UNSAFE_componentWillMount() {
if (!this.props.sequence) { setActiveSequenceByName(); }
}

View File

@ -40,13 +40,13 @@ describe("Pin tile support functions", () => {
}
it("PIN_MODES", () => {
expect(PIN_MODES[0].value).toEqual(1);
expect(PIN_MODES[0].label).toEqual("Analog");
expect(PIN_MODES()[0].value).toEqual(1);
expect(PIN_MODES()[0].label).toEqual("Analog");
});
it("PIN_VALUES", () => {
expect(PIN_VALUES[0].value).toEqual(1);
expect(PIN_VALUES[0].label).toEqual("ON");
expect(PIN_VALUES()[0].value).toEqual(1);
expect(PIN_VALUES()[0].label).toEqual("ON");
});
it("currentModeSelection()", () => {
@ -67,7 +67,7 @@ describe("Pin tile support functions", () => {
const p = fakeProps();
p.currentStep.args.pin_mode = 0;
p.currentStep.args.pin_value = 0;
setPinMode(PIN_MODES[0], p);
setPinMode(PIN_MODES()[0], p);
const step = p.currentStep;
mockEditStep.mock.calls[0][0].executor(step);
expect(step.args.pin_mode).toEqual(1);
@ -85,7 +85,7 @@ describe("Pin tile support functions", () => {
it("setPinValue()", () => {
const p = fakeProps();
p.currentStep.args.pin_value = 5;
setPinValue(PIN_VALUES[0], p);
setPinValue(PIN_VALUES()[0], p);
const step = p.currentStep;
mockEditStep.mock.calls[0][0].executor(step);
expect(step.args.pin_value).toEqual(1);

View File

@ -15,12 +15,12 @@ enum PinMode {
const isPinMode = (x: any): x is ALLOWED_PIN_MODES =>
Object.values(PinMode).includes(x);
export const PIN_MODES = [
export const PIN_MODES = () => [
{ value: PinMode.analog, label: t("Analog") },
{ value: PinMode.digital, label: t("Digital") }
];
export const PIN_VALUES = [
export const PIN_VALUES = () => [
{ value: 1, label: t("ON") },
{ value: 0, label: t("OFF") }
];

View File

@ -19,7 +19,7 @@ export function PinMode(props: StepParams) {
key={JSON.stringify(props.currentSequence)}
onChange={(x) => setPinMode(x, props)}
selectedItem={currentModeSelection(props.currentStep)}
list={PIN_MODES} />
list={PIN_MODES()} />
</Col>;
}

View File

@ -33,7 +33,7 @@ export function TileWritePin(props: StepParams) {
key={JSON.stringify(props.currentSequence)}
onChange={x => setPinValue(x, props)}
selectedItem={currentValueSelection(currentStep)}
list={PIN_VALUES} />;
list={PIN_VALUES()} />;
const className = "write-pin-step";
const { pin_number } = currentStep.args;

View File

@ -1,6 +1,5 @@
import * as React from "react";
import { ToolListAndFormProps } from "../interfaces";
import {
Row,
Col,
@ -73,7 +72,7 @@ export class ToolForm extends React.Component<ToolListAndFormProps, {}> {
<Col xs={2}>
<button
className={`fb-button red ${inSlotClass} del-button`}
title={isActive(tool) ? t("in slot") : ""}
title={isActive(tool) ? t("in slot") : t("Delete")}
onClick={() => dispatch(destroy(tool.uuid))}>
<i className="fa fa-times"></i>
</button>

View File

@ -56,6 +56,7 @@ export function ToolSlotRow(props: ToolSlotRowProps) {
<Col xs={1}>
<button
className="red fb-button del-button"
title={t("Delete")}
onClick={() => dispatch(destroy(slot.uuid))}>
<i className="fa fa-times" />
</button>