Merge pull request #1110 from gabrielburnworth/staging

Misc bug fixes
pull/1111/head
Rick Carlino 2019-02-11 08:15:59 -06:00 committed by GitHub
commit e6b154602b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 156 additions and 54 deletions

View File

@ -1,3 +1,4 @@
<!DOCTYPE html>
<html>
<head>
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />

View File

@ -385,7 +385,8 @@ CREATE TABLE public.farmware_installations (
url character varying,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL,
package character varying(80)
package character varying(80),
package_error character varying
);

View File

@ -70,7 +70,7 @@ export class Account extends React.Component<Props, State> {
const deleteAcct =
(password: string) => this.props.dispatch(deleteUser({ password }));
return <Page className="account">
return <Page className="account-page">
<Col xs={12} sm={6} smOffset={3}>
<Row>
<Settings

View File

@ -55,6 +55,7 @@
&.green {
background-color: $green;
box-shadow: 0 2px 0px 0px darken($green, 12%);
&:focus,
&:hover,
&.active {
background-color: darken($green, 5%) !important;
@ -63,6 +64,7 @@
&.blue {
background-color: $blue;
box-shadow: 0 2px 0px 0px darken($blue, 12%);
&:focus,
&:hover,
&.active {
background-color: darken($blue, 5%) !important;
@ -71,6 +73,7 @@
&.red {
background-color: $red;
box-shadow: 0 2px 0px 0px darken($red, 12%);
&:focus,
&:hover,
&.active {
background-color: darken($red, 5%) !important;
@ -83,6 +86,7 @@
&.disabled {
background: $gray !important;
}
&:focus,
&:hover,
&.active {
background-color: darken($medium_gray, 5%) !important;
@ -92,6 +96,7 @@
color: $dark_gray;
background-color: $yellow;
box-shadow: 0 2px 0px 0px darken($yellow, 12%);
&:focus,
&:hover,
&.active {
background-color: darken($yellow, 5%) !important;
@ -101,6 +106,7 @@
color: $dark_gray;
background-color: $orange;
box-shadow: 0 2px 0px 0px darken($orange, 12%);
&:focus,
&:hover,
&.active {
background-color: darken($orange, 5%) !important;
@ -109,6 +115,7 @@
&.magenta {
background-color: $magenta;
box-shadow: 0 2px 0px 0px darken($magenta, 12%);
&:focus,
&:hover,
&.active {
background-color: darken($magenta, 5%) !important;
@ -117,6 +124,7 @@
&.cyan {
background-color: $cyan;
box-shadow: 0 2px 0px 0px darken($cyan, 12%);
&:focus,
&:hover,
&.active {
background-color: darken($cyan, 5%) !important;
@ -125,6 +133,7 @@
&.brown {
background-color: $brown;
box-shadow: 0 2px 0px 0px darken($brown, 12%);
&:focus,
&:hover,
&.active {
background-color: darken($brown, 5%) !important;
@ -133,6 +142,7 @@
&.purple {
background-color: $purple;
box-shadow: 0 2px 0px 0px darken($purple, 12%);
&:focus,
&:hover,
&.active {
background-color: darken($purple, 5%) !important;
@ -141,6 +151,7 @@
&.pink {
background-color: $pink;
box-shadow: 0 2px 0px 0px darken($pink, 12%);
&:focus,
&:hover,
&.active {
background-color: darken($pink, 5%) !important;
@ -159,6 +170,7 @@
&.pseudo-disabled {
background: $medium_light_gray !important;
box-shadow: 0 2px 0px 0px lighten($medium_light_gray, 5%) !important;
&:focus,
&:hover,
&.active {
background: $medium_light_gray !important;
@ -178,6 +190,7 @@
&.gray {
background-color: $medium_light_gray !important;
box-shadow: 0 2px 0px 0px darken($medium_light_gray, 12%);
&:focus,
&:hover,
&.active {
background-color: darken($medium_light_gray, 5%) !important;

View File

@ -575,9 +575,10 @@ ul {
.unavailable {
position: relative;
z-index: 10;
width: 100%;
opacity: 0.40;
pointer-events: none;
* {
pointer-events: none;
}
&.banner {
&:after {
content: "Not available when device is offline.";
@ -868,6 +869,12 @@ ul {
}
}
.account-page {
label {
margin-top: 5px;
}
}
.release-notes-button {
font-weight: bold;
cursor: pointer;
@ -966,6 +973,10 @@ ul {
}
}
.farmware-name-manual-input {
margin-top: 1rem;
}
.checkbox-row {
margin-top: 1rem;
label {
@ -1037,7 +1048,8 @@ ul {
background: $pink;
border: 1px solid $red;
border-radius: 5px;
label, p {
label,
p {
margin: 0.5rem;
color: $red;
}

View File

@ -89,6 +89,9 @@
}
.farmware-button {
div {
float: right;
}
p {
float: right;
margin-top: 0.75rem;

View File

@ -3,7 +3,7 @@
left: 0;
right: 0;
z-index: 99;
height: 9rem;
height: 8.9rem;
background: $dark_gray;
box-shadow: 0 4px 10px rgba(0, 0, 0, .2);
}
@ -43,6 +43,9 @@ nav {
padding: 2rem 1rem;
letter-spacing: 1.2px;
transition: font-weight 0.2s ease;
&:focus {
font-weight: bold;
}
&:hover {
font-weight: bold;
color: $white;

View File

@ -4,6 +4,7 @@
left: 0;
right: 0;
z-index: 3;
height: 3.25rem;
background: $black;
opacity: 0.9;
.bp3-collapse {

View File

@ -22,6 +22,7 @@
}
.widget-header {
height: 3.3rem;
background: $dark_gray;
letter-spacing: .05rem;
padding: .75rem 1rem;

View File

@ -1,6 +1,8 @@
import * as React from "react";
import { NetworkState } from "../connectivity/interfaces";
import { SyncStatus } from "farmbot";
import { t } from "i18next";
import { Content } from "../constants";
/** Properties for the <MustBeOnline/> element. */
export interface MBOProps {
@ -23,6 +25,10 @@ export function MustBeOnline(props: MBOProps) {
if ((botUp && netUp) || lockOpen) {
return <div> {children} </div>;
} else {
return <div className={`unavailable ${banner}`}> {children} </div>;
return <div
className={`unavailable ${banner}`}
title={t(Content.NOT_AVAILABLE_WHEN_OFFLINE)}>
{children}
</div>;
}
}

View File

@ -218,21 +218,22 @@ export class EditFEForm extends React.Component<EditFEProps, State> {
allowedDeclarations={AllowedDeclaration.variable}
shouldDisplay={this.props.shouldDisplay} />
executableSet = (e: DropDownItem) => {
if (e.value) {
const { executable_type } = this.props.farmEvent.body;
if (executable_type === "Regimen" &&
executableType(e.headingId) === "Sequence") {
executableSet = (ddi: DropDownItem) => {
if (ddi.value) {
const prev_executable_type = this.props.farmEvent.body.executable_type;
const next_executable_type = executableType(ddi.headingId);
if (prev_executable_type === "Regimen" &&
next_executable_type === "Sequence") {
error(t("Cannot change from a Regimen to a Sequence."));
history.push("/app/designer/farm_events");
} else {
const { uuid } =
this.props.findExecutable(executable_type, parseInt("" + e.value));
const { uuid } = this.props.findExecutable(
next_executable_type, parseInt("" + ddi.value));
const varData = this.props.resources.sequenceMetas[uuid];
const update: State = {
fe: {
executable_type: executableType(e.headingId),
executable_id: (e.value || "").toString(),
executable_type: next_executable_type,
executable_id: (ddi.value || "").toString(),
},
specialStatusLocal: SpecialStatus.DIRTY
};

View File

@ -21,7 +21,7 @@ describe("mapStateToProps()", () => {
it("sync status unknown", () => {
const props = mapStateToProps(fakeState());
expect(props.botToMqttStatus).toEqual("up");
expect(props.botToMqttStatus).toEqual("down");
});
it("currentImage undefined", () => {
@ -129,6 +129,15 @@ describe("mapStateToProps()", () => {
const props = mapStateToProps(state);
expect(props.imageJobs).toEqual([]);
});
it("returns bot status", () => {
const state = fakeState();
state.bot.hardware.informational_settings.sync_status = "sync_now";
state.bot.connectivity["bot.mqtt"] = { state: "up", at: "" };
const props = mapStateToProps(state);
expect(props.syncStatus).toEqual("sync_now");
expect(props.botToMqttStatus).toEqual("up");
});
});
describe("saveOrEditFarmwareEnv()", () => {

View File

@ -28,11 +28,19 @@ export class CameraCalibration extends
render() {
return <div className="weed-detector">
<button
onClick={this.props.dispatch(calibrate)}
className="fb-button green farmware-button" >
{t("Calibrate")}
</button>
<div className="farmware-button">
<MustBeOnline
syncStatus={this.props.syncStatus}
networkState={this.props.botToMqttStatus}
hideBanner={true}
lockOpen={process.env.NODE_ENV !== "production"}>
<button
onClick={this.props.dispatch(calibrate)}
className="fb-button green farmware-button" >
{t("Calibrate")}
</button>
</MustBeOnline>
</div>
<Row>
<Col sm={12}>
<MustBeOnline

View File

@ -23,6 +23,8 @@ describe("<Photos/>", () => {
dispatch: jest.fn(),
timeOffset: 0,
imageJobs: [],
botToMqttStatus: "up",
syncStatus: "synced",
});
it("shows photo", () => {

View File

@ -1,4 +1,5 @@
import { TaggedImage, JobProgress } from "farmbot";
import { TaggedImage, JobProgress, SyncStatus } from "farmbot";
import { NetworkState } from "../../connectivity/interfaces";
export interface ImageFlipperProps {
onFlip(uuid: string | undefined): void;
@ -18,4 +19,14 @@ export interface PhotosProps {
currentImage: TaggedImage | undefined;
timeOffset: number;
imageJobs: JobProgress[];
botToMqttStatus: NetworkState;
syncStatus: SyncStatus | undefined;
}
export interface PhotoButtonsProps {
takePhoto: () => void,
deletePhoto: () => void,
imageJobs: JobProgress[],
botToMqttStatus: NetworkState;
syncStatus: SyncStatus | undefined;
}

View File

@ -3,7 +3,7 @@ import moment from "moment";
import { t } from "i18next";
import { success, error } from "farmbot-toastr";
import { ImageFlipper } from "./image_flipper";
import { PhotosProps } from "./interfaces";
import { PhotosProps, PhotoButtonsProps } from "./interfaces";
import { getDevice } from "../../device";
import { Content } from "../../constants";
import { selectImage } from "./actions";
@ -12,8 +12,9 @@ import { destroy } from "../../api/crud";
import {
downloadProgress
} from "../../devices/components/fbos_settings/os_update_button";
import { JobProgress, TaggedImage } from "farmbot";
import { TaggedImage } from "farmbot";
import { startCase } from "lodash";
import { MustBeOnline } from "../../devices/must_be_online";
interface MetaInfoProps {
/** Default conversion is `attr_name ==> Attr Name`.
@ -48,18 +49,20 @@ const PhotoMetaData = ({ image }: { image: TaggedImage | undefined }) =>
obj={{ image: t("No meta data.") }} />}
</div>;
const PhotoButtons = (props: {
takePhoto: () => void,
deletePhoto: () => void,
imageJobs: JobProgress[]
}) => {
const PhotoButtons = (props: PhotoButtonsProps) => {
const imageUploadJobProgress = downloadProgress(props.imageJobs[0]);
return <div className="farmware-button">
<button
className="fb-button green"
onClick={props.takePhoto}>
{t("Take Photo")}
</button>
<MustBeOnline
syncStatus={props.syncStatus}
networkState={props.botToMqttStatus}
hideBanner={true}
lockOpen={process.env.NODE_ENV !== "production"}>
<button
className="fb-button green"
onClick={props.takePhoto}>
{t("Take Photo")}
</button>
</MustBeOnline>
<button
className="fb-button red"
onClick={props.deletePhoto}>
@ -115,6 +118,8 @@ export class Photos extends React.Component<PhotosProps, {}> {
render() {
return <div className="photos">
<PhotoButtons
syncStatus={this.props.syncStatus}
botToMqttStatus={this.props.botToMqttStatus}
takePhoto={this.takePhoto}
deletePhoto={this.deletePhoto}
imageJobs={this.props.imageJobs} />

View File

@ -119,6 +119,8 @@ export class FarmwarePage extends React.Component<FarmwareProps, {}> {
case "take_photo":
case "photos":
return <Photos
syncStatus={this.props.syncStatus}
botToMqttStatus={this.props.botToMqttStatus}
timeOffset={this.props.timeOffset}
dispatch={this.props.dispatch}
images={this.props.images}

View File

@ -112,17 +112,21 @@ export function mapStateToProps(props: Everything): FarmwareProps {
.reverse()
.value();
const bot2mqtt = props.bot.connectivity["bot.mqtt"];
const botToMqttStatus = bot2mqtt ? bot2mqtt.state : "down";
const syncStatus = props.bot.hardware.informational_settings.sync_status;
return {
timeOffset: maybeGetTimeOffset(props.resources.index),
currentFarmware,
farmwares,
botToMqttStatus: "up",
botToMqttStatus,
env: prepopulateEnv(env),
user_env: env,
dispatch: props.dispatch,
currentImage,
images,
syncStatus: "synced",
syncStatus,
webAppConfig: conf ? conf.body : {},
firstPartyFarmwareNames,
shouldDisplay,

View File

@ -56,11 +56,17 @@ export class WeedDetector
render() {
return <div className="weed-detector">
<div className="farmware-button">
<button
onClick={this.props.dispatch(test)}
className="fb-button green">
{t("detect weeds")}
</button>
<MustBeOnline
syncStatus={this.props.syncStatus}
networkState={this.props.botToMqttStatus}
hideBanner={true}
lockOpen={process.env.NODE_ENV !== "production"}>
<button
onClick={this.props.dispatch(test)}
className="fb-button green">
{t("detect weeds")}
</button>
</MustBeOnline>
<button
onClick={this.clearWeeds}
className="fb-button red">

View File

@ -44,22 +44,23 @@ describe("<TileSendMessage/>", () => {
const labels = block.find("label");
const buttons = block.find("button");
expect(inputs.length).toEqual(6);
expect(labels.length).toEqual(5);
expect(labels.length).toEqual(6);
expect(buttons.length).toEqual(1);
expect(inputs.first().props().placeholder).toEqual("Send Message");
expect(labels.at(0).text()).toEqual("Message");
expect(inputs.at(1).props().value).toEqual("send this message");
expect(labels.at(1).text()).toEqual("type");
expect(buttons.at(0).text()).toEqual("Info");
expect(labels.at(1).text()).toEqual("Ticker Notification");
expect(labels.at(2).text()).toEqual("Ticker Notification");
expect(inputs.at(2).props().checked).toBeTruthy();
expect(inputs.at(2).props().disabled).toBeTruthy();
expect(labels.at(2).text()).toEqual("Toast Pop Up");
expect(labels.at(3).text()).toEqual("Toast Pop Up");
expect(inputs.at(3).props().checked).toBeTruthy();
expect(inputs.at(3).props().disabled).toBeFalsy();
expect(labels.at(3).text()).toEqual("Email");
expect(labels.at(4).text()).toEqual("Email");
expect(inputs.at(4).props().checked).toBeFalsy();
expect(inputs.at(4).props().disabled).toBeFalsy();
expect(labels.at(4).text()).toEqual("Speak");
expect(labels.at(5).text()).toEqual("Speak");
expect(inputs.at(5).props().checked).toBeFalsy();
expect(inputs.at(5).props().disabled).toBeFalsy();
});

View File

@ -77,7 +77,7 @@ export function TileExecuteScript(props: StepParams) {
allowEmpty={true}
customNullLabel={"Manual Input"} />
{!isInstalled(farmwareName) &&
<div>
<div className="farmware-name-manual-input">
<label>{t("Manual input")}</label>
<StepInputBox dispatch={dispatch}
index={index}

View File

@ -120,6 +120,7 @@ export class RefactoredSendMessage
field="message" />
<div className="bottom-content">
<div className="channel-options">
<label>{t("type")}</label>
<FBSelect
onChange={this.setMsgType}
selectedItem={this.currentSelection}

View File

@ -28,6 +28,7 @@ export interface BIProps {
hidden?: boolean;
error?: string;
title?: string;
autoFocus?: boolean;
}
interface BIState {
@ -110,6 +111,7 @@ export class BlurableInput extends React.Component<BIProps, Partial<BIState>> {
className: (this.props.className || "") + (this.error ? " error" : ""),
title: this.props.title || "",
placeholder: this.props.placeholder,
autoFocus: this.props.autoFocus,
};
}

View File

@ -133,6 +133,17 @@ describe("util", () => {
raw_encoders: { x: undefined, y: undefined, z: undefined }
});
});
it("returns valid location_data object when a partial is provided", () => {
const result = Util.validBotLocationData(
// tslint:disable-next-line:no-any
{ raw_encoders: { x: 123 } } as any);
expect(result).toEqual({
position: { x: undefined, y: undefined, z: undefined },
scaled_encoders: { x: undefined, y: undefined, z: undefined },
raw_encoders: { x: 123, y: undefined, z: undefined }
});
});
});
describe("fancyDebug()", () => {

View File

@ -179,14 +179,11 @@ export function scrollToBottom(elementId: string) {
export function validBotLocationData(
botLocationData: BotLocationData | undefined): BotLocationData {
if (botLocationData && botLocationData.position && botLocationData.position.x) {
return botLocationData;
}
return {
return betterMerge({
position: { x: undefined, y: undefined, z: undefined },
scaled_encoders: { x: undefined, y: undefined, z: undefined },
raw_encoders: { x: undefined, y: undefined, z: undefined },
};
}, botLocationData);
}
/**

View File

@ -15,7 +15,8 @@
"typecheck": "./node_modules/typescript/bin/tsc --noEmit",
"tslint": "./node_modules/tslint/bin/tslint --project .",
"sass-lint": "./node_modules/sass-lint/bin/sass-lint.js -c .sass-lint.yml -v -q",
"sass-check": "./node_modules/sass/sass.js --no-source-map frontend/css/_index.scss sass.log"
"sass-check": "./node_modules/sass/sass.js --no-source-map frontend/css/_index.scss sass.log",
"linters": "npm run typecheck && npm run tslint && npm run sass-lint && npm run sass-check"
},
"keywords": [
"farmbot"