message fixes and updates
parent
e032acddfd
commit
c690bf760b
|
@ -15,6 +15,12 @@ module Devices
|
|||
device = Device.create!({name: "Farmbot"}.merge(inputs.except(:user)))
|
||||
Enigmas::Create.run!(device: device,
|
||||
problem_tag: Enigma::SEED_DATA)
|
||||
Enigmas::Create.run!(device: device,
|
||||
problem_tag: Enigma::TOUR)
|
||||
Enigmas::Create.run!(device: device,
|
||||
problem_tag: Enigma::USER)
|
||||
Enigmas::Create.run!(device: device,
|
||||
problem_tag: Enigma::DOCUMENTATION)
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
# TODO: This is a really, really, really old
|
||||
|
|
|
@ -316,12 +316,13 @@ export namespace ToolTips {
|
|||
|
||||
// Tools
|
||||
export const TOOL_LIST =
|
||||
trim(`This is a list of all your FarmBot tools and seed containers. Click the Edit button
|
||||
to add, edit, or delete tools or seed containers.`);
|
||||
trim(`This is a list of all your FarmBot tools and seed containers.
|
||||
Click the Edit button to add, edit, or delete tools or seed containers.`);
|
||||
|
||||
export const TOOLBAY_LIST =
|
||||
trim(`Tool slots are where you store your FarmBot tools and seed containers, which should be
|
||||
reflective of your real FarmBot hardware configuration.`);
|
||||
trim(`Tool slots are where you store your FarmBot tools and seed
|
||||
containers, which should be reflective of your real FarmBot hardware
|
||||
configuration.`);
|
||||
|
||||
// Logs
|
||||
export const LOGS =
|
||||
|
@ -420,6 +421,11 @@ export namespace Content {
|
|||
trim(`When you're finished with a message, press the x button in the
|
||||
top right of the card to dismiss it.`);
|
||||
|
||||
export const FIRMWARE_MISSING =
|
||||
trim(`Please choose a firmware version to install. Your choice should be
|
||||
based on the type of electronics in your FarmBot according to the reference
|
||||
table below.`);
|
||||
|
||||
// App Settings
|
||||
export const CONFIRM_STEP_DELETION =
|
||||
trim(`Show a confirmation dialog when deleting a sequence step.`);
|
||||
|
|
|
@ -939,7 +939,7 @@ ul {
|
|||
}
|
||||
|
||||
.messages-page {
|
||||
max-width: 600px;
|
||||
max-width: 785px;
|
||||
padding-left: 2rem !important;
|
||||
padding-right: 2rem !important;
|
||||
.link-to-logs {
|
||||
|
@ -1104,7 +1104,7 @@ ul {
|
|||
|
||||
.tour-list {
|
||||
margin: auto;
|
||||
width: 75%;
|
||||
max-width: 300px;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
|
@ -1171,9 +1171,23 @@ ul {
|
|||
box-shadow: 0px 2px 5px $medium_gray;
|
||||
background: $off_white;
|
||||
.problem-alert-title {
|
||||
position: relative;
|
||||
margin-bottom: 1rem;
|
||||
line-height: 1rem;
|
||||
.fa-exclamation-triangle,
|
||||
.fa-check-square,
|
||||
.fa-info-circle {
|
||||
font-size: 1.6rem;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
.fa-exclamation-triangle {
|
||||
color: $orange;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
.fa-check-square {
|
||||
color: $green;
|
||||
}
|
||||
.fa-info-circle {
|
||||
color: $blue;
|
||||
}
|
||||
h3 {
|
||||
color: $dark_gray;
|
||||
|
@ -1182,13 +1196,16 @@ ul {
|
|||
}
|
||||
p {
|
||||
display: inline;
|
||||
padding: 1rem;
|
||||
color: $medium_gray;
|
||||
font-size: 1.2rem;
|
||||
margin-left: 2rem;
|
||||
white-space: pre;
|
||||
}
|
||||
.fa-times {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
color: $medium_light_gray;
|
||||
float: right;
|
||||
&:hover {
|
||||
color: $dark_gray;
|
||||
}
|
||||
|
@ -1215,5 +1232,32 @@ ul {
|
|||
float: none;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.fb-button {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.firmware-alerts {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.firmware-hardware-choice-table {
|
||||
margin: 2rem;
|
||||
margin-top: 1rem;
|
||||
width: 93%;
|
||||
border: 1px solid $gray;
|
||||
font-size: 1.2rem;
|
||||
th {
|
||||
background: $light_gray;
|
||||
font-weight: normal;
|
||||
}
|
||||
td {
|
||||
background: $off_white;
|
||||
color: $medium_gray;
|
||||
}
|
||||
code {
|
||||
background: $light_gray;
|
||||
color: $dark_gray;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,21 @@ export interface FirmwareHardwareStatusDetailsProps {
|
|||
dispatch: Function;
|
||||
}
|
||||
|
||||
export interface FlashFirmwareBtnProps {
|
||||
apiFirmwareValue: string | undefined;
|
||||
botOnline: boolean;
|
||||
}
|
||||
|
||||
export const FlashFirmwareBtn = (props: FlashFirmwareBtnProps) => {
|
||||
const { apiFirmwareValue } = props;
|
||||
return <button className="fb-button yellow"
|
||||
disabled={!apiFirmwareValue || !props.botOnline}
|
||||
onClick={() => isFwHardwareValue(apiFirmwareValue) &&
|
||||
flashFirmware(apiFirmwareValue)}>
|
||||
{t("flash firmware")}
|
||||
</button>;
|
||||
};
|
||||
|
||||
export interface FirmwareActionsProps {
|
||||
apiFirmwareValue: string | undefined;
|
||||
botOnline: boolean;
|
||||
|
@ -51,12 +66,7 @@ export const FirmwareActions = (props: FirmwareActionsProps) => {
|
|||
{trim(`${t("Flash the")} ${lookup(apiFirmwareValue) || ""}
|
||||
${t("firmware to your device")}:`)}
|
||||
</p>
|
||||
<button className="fb-button yellow"
|
||||
disabled={!apiFirmwareValue || !props.botOnline}
|
||||
onClick={() => isFwHardwareValue(apiFirmwareValue) &&
|
||||
flashFirmware(apiFirmwareValue)}>
|
||||
{t("flash firmware")}
|
||||
</button>
|
||||
<FlashFirmwareBtn {...props} />
|
||||
</div>;
|
||||
};
|
||||
|
||||
|
@ -98,7 +108,7 @@ export const FirmwareHardwareStatus = (props: FirmwareHardwareStatusProps) => {
|
|||
const { firmware_hardware } = props.bot.hardware.configuration;
|
||||
const status = props.apiFirmwareValue == firmware_hardware &&
|
||||
props.apiFirmwareValue == boardType(firmware_version);
|
||||
return <Popover position={Position.BOTTOM}>
|
||||
return <Popover position={Position.TOP}>
|
||||
<FirmwareHardwareStatusIcon
|
||||
firmwareHardware={firmware_hardware}
|
||||
status={status} />
|
||||
|
|
|
@ -53,7 +53,7 @@ describe("<Alerts />", () => {
|
|||
p.alerts = [FIRMWARE_MISSING_ALERT, SEED_DATA_MISSING_ALERT];
|
||||
const wrapper = mount(<Alerts {...p} />);
|
||||
expect(wrapper.text()).toContain("2");
|
||||
expect(wrapper.text()).toContain("Your device has no firmware installed");
|
||||
expect(wrapper.text()).toContain("Your device has no firmware");
|
||||
expect(wrapper.text()).toContain("Choose your FarmBot");
|
||||
});
|
||||
|
||||
|
@ -89,7 +89,7 @@ describe("<FirmwareAlerts />", () => {
|
|||
};
|
||||
const wrapper = mount(<FirmwareAlerts {...p} />);
|
||||
expect(wrapper.text()).toContain("1");
|
||||
expect(wrapper.text()).toContain("Your device has no firmware installed");
|
||||
expect(wrapper.text()).toContain("Your device has no firmware");
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
jest.mock("../../devices/actions", () => ({ updateConfig: jest.fn() }));
|
||||
|
||||
jest.mock("../../api/crud", () => ({ destroy: jest.fn() }));
|
||||
|
||||
import * as React from "react";
|
||||
import { mount } from "enzyme";
|
||||
import { AlertCard } from "../cards";
|
||||
import { AlertCard, changeFirmwareHardware } from "../cards";
|
||||
import { AlertCardProps } from "../interfaces";
|
||||
import { fakeTimeSettings } from "../../__test_support__/fake_time_settings";
|
||||
import { FBSelect } from "../../ui";
|
||||
import { destroy } from "../../api/crud";
|
||||
import { updateConfig } from "../../devices/actions";
|
||||
|
||||
describe("<AlertCard />", () => {
|
||||
const fakeProps = (): AlertCardProps => ({
|
||||
|
@ -35,9 +38,8 @@ describe("<AlertCard />", () => {
|
|||
const p = fakeProps();
|
||||
p.alert.problem_tag = "farmbot_os.firmware.missing";
|
||||
const wrapper = mount(<AlertCard {...p} />);
|
||||
expect(wrapper.text()).toContain("Firmware missing");
|
||||
wrapper.find(".fa-times").simulate("click");
|
||||
expect(destroy).not.toHaveBeenCalled();
|
||||
expect(wrapper.text()).toContain("Your device has no firmware");
|
||||
expect(wrapper.find(".fa-times").length).toEqual(0);
|
||||
});
|
||||
|
||||
it("renders seed data card", () => {
|
||||
|
@ -69,3 +71,10 @@ describe("<AlertCard />", () => {
|
|||
expect(wrapper.text()).toContain("Learn");
|
||||
});
|
||||
});
|
||||
|
||||
describe("changeFirmwareHardware()", () => {
|
||||
it("changes firmware hardware value", () => {
|
||||
changeFirmwareHardware(jest.fn())({ label: "Arduino", value: "arduino" });
|
||||
expect(updateConfig).toHaveBeenCalledWith({ firmware_hardware: "arduino" });
|
||||
});
|
||||
});
|
||||
|
|
|
@ -17,27 +17,29 @@ export const sortAlerts = (alerts: Alert[]): Alert[] =>
|
|||
export const FirmwareAlerts = (props: FirmwareAlertsProps) => {
|
||||
const alerts = betterCompact(Object.values(props.bot.hardware.enigmas || {}));
|
||||
const firmwareAlerts = sortAlerts(alerts)
|
||||
.filter(x => x.problem_tag && x.priority && x.created_at)
|
||||
.filter(x => splitProblemTag(x.problem_tag).noun === "firmware");
|
||||
return <div className="firmware-alerts">
|
||||
{firmwareAlerts.filter(x => x.problem_tag && x.priority && x.created_at)
|
||||
.map((x, i) =>
|
||||
<AlertCard key={i}
|
||||
alert={x}
|
||||
dispatch={props.dispatch}
|
||||
apiFirmwareValue={props.apiFirmwareValue}
|
||||
timeSettings={props.timeSettings} />)}
|
||||
{firmwareAlerts.map((x, i) =>
|
||||
<AlertCard key={i}
|
||||
alert={x}
|
||||
dispatch={props.dispatch}
|
||||
apiFirmwareValue={props.apiFirmwareValue}
|
||||
timeSettings={props.timeSettings} />)}
|
||||
</div>;
|
||||
};
|
||||
|
||||
export const Alerts = (props: AlertsProps) =>
|
||||
<div className="problem-alerts">
|
||||
<div className="problem-alerts-content">
|
||||
{sortAlerts(props.alerts).map((x, i) =>
|
||||
<AlertCard key={i}
|
||||
alert={x}
|
||||
dispatch={props.dispatch}
|
||||
apiFirmwareValue={props.apiFirmwareValue}
|
||||
timeSettings={props.timeSettings}
|
||||
findApiAlertById={props.findApiAlertById} />)}
|
||||
{sortAlerts(props.alerts)
|
||||
.filter(x => x.problem_tag && x.priority && x.created_at)
|
||||
.map((x, i) =>
|
||||
<AlertCard key={i}
|
||||
alert={x}
|
||||
dispatch={props.dispatch}
|
||||
apiFirmwareValue={props.apiFirmwareValue}
|
||||
timeSettings={props.timeSettings}
|
||||
findApiAlertById={props.findApiAlertById} />)}
|
||||
</div>
|
||||
</div>;
|
||||
|
|
|
@ -8,13 +8,17 @@ import {
|
|||
} from "./interfaces";
|
||||
import { formatLogTime } from "../logs";
|
||||
import {
|
||||
FirmwareActions
|
||||
FlashFirmwareBtn
|
||||
} from "../devices/components/fbos_settings/firmware_hardware_status";
|
||||
import { DropDownItem, Row, Col, FBSelect, docLink } from "../ui";
|
||||
import { Content } from "../constants";
|
||||
import { TourList } from "../help/tour_list";
|
||||
import { splitProblemTag } from "./alerts";
|
||||
import { destroy } from "../api/crud";
|
||||
import {
|
||||
isFwHardwareValue
|
||||
} from "../devices/components/fbos_settings/board_type";
|
||||
import { updateConfig } from "../devices/actions";
|
||||
|
||||
export const AlertCard = (props: AlertCardProps) => {
|
||||
const { alert, timeSettings, findApiAlertById, dispatch } = props;
|
||||
|
@ -34,23 +38,22 @@ export const AlertCard = (props: AlertCardProps) => {
|
|||
case "api.documentation.unread":
|
||||
return <DocumentationUnread {...commonProps} />;
|
||||
default:
|
||||
return UnknownAlert(commonProps);
|
||||
return <UnknownAlert {...commonProps} />;
|
||||
}
|
||||
};
|
||||
const dismissAlert = (props: DismissAlertProps) => () =>
|
||||
(props.id && props.findApiAlertById && props.dispatch)
|
||||
? props.dispatch(destroy(props.findApiAlertById(props.id)))
|
||||
: () => { };
|
||||
(props.id && props.findApiAlertById && props.dispatch) &&
|
||||
props.dispatch(destroy(props.findApiAlertById(props.id)));
|
||||
|
||||
const AlertCardTemplate = (props: AlertCardTemplateProps) => {
|
||||
const { alert, findApiAlertById, dispatch } = props;
|
||||
return <div className={`problem-alert ${props.className}`}>
|
||||
<div className="problem-alert-title">
|
||||
<i className="fa fa-exclamation-triangle" />
|
||||
<i className={`fa fa-${props.iconName || "exclamation-triangle"}`} />
|
||||
<h3>{t(props.title)}</h3>
|
||||
<p>{formatLogTime(alert.created_at, props.timeSettings)}</p>
|
||||
<i className="fa fa-times"
|
||||
onClick={dismissAlert({ id: alert.id, findApiAlertById, dispatch })} />
|
||||
{alert.id && <i className="fa fa-times"
|
||||
onClick={dismissAlert({ id: alert.id, findApiAlertById, dispatch })} />}
|
||||
</div>
|
||||
<div className="problem-alert-content">
|
||||
<p>{t(props.message)}</p>
|
||||
|
@ -74,18 +77,77 @@ const UnknownAlert = (props: CommonAlertCardProps) => {
|
|||
findApiAlertById={props.findApiAlertById} />;
|
||||
};
|
||||
|
||||
const FIRMWARE_CHOICES: DropDownItem[] = [
|
||||
{ label: "Arduino/RAMPS (Genesis v1.2)", value: "arduino" },
|
||||
{ label: "Farmduino (Genesis v1.3)", value: "farmduino" },
|
||||
{ label: "Farmduino (Genesis v1.4)", value: "farmduino_k14" },
|
||||
];
|
||||
|
||||
const FIRMWARE_CHOICES_DDI: { [x: string]: DropDownItem } = {};
|
||||
FIRMWARE_CHOICES.map(x => FIRMWARE_CHOICES_DDI[x.value] = x);
|
||||
|
||||
const FirmwareChoiceTable = () =>
|
||||
<table className="firmware-hardware-choice-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{t("FarmBot Version")}</th>
|
||||
<th>{t("Electronics Board")}</th>
|
||||
<th>{t("Firmware Name")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{"Genesis v1.2"}</td>
|
||||
<td>{"RAMPS"}</td>
|
||||
<td><code>{FIRMWARE_CHOICES_DDI["arduino"].label}</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{"Genesis v1.3"}</td>
|
||||
<td>{"Farmduino"}</td>
|
||||
<td><code>{FIRMWARE_CHOICES_DDI["farmduino"].label}</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{"Genesis v1.4"}</td>
|
||||
<td>{"Farmduino"}</td>
|
||||
<td><code>{FIRMWARE_CHOICES_DDI["farmduino_k14"].label}</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>;
|
||||
|
||||
export const changeFirmwareHardware = (dispatch: Function | undefined) =>
|
||||
(ddi: DropDownItem) => {
|
||||
if (isFwHardwareValue(ddi.value)) {
|
||||
dispatch && dispatch(updateConfig({ firmware_hardware: ddi.value }));
|
||||
}
|
||||
};
|
||||
|
||||
const FirmwareMissing = (props: FirmwareMissingProps) =>
|
||||
<AlertCardTemplate
|
||||
alert={props.alert}
|
||||
className={"firmware-missing-alert"}
|
||||
title={t("Firmware missing")}
|
||||
message={t("Your device has no firmware installed.")}
|
||||
title={t("Your device has no firmware")}
|
||||
message={t(Content.FIRMWARE_MISSING)}
|
||||
timeSettings={props.timeSettings}
|
||||
dispatch={props.dispatch}
|
||||
findApiAlertById={props.findApiAlertById}>
|
||||
<FirmwareActions
|
||||
apiFirmwareValue={props.apiFirmwareValue}
|
||||
botOnline={true} />
|
||||
<Row>
|
||||
<FirmwareChoiceTable />
|
||||
<Col xs={4}>
|
||||
<label>{t("Choose Firmware")}</label>
|
||||
</Col>
|
||||
<Col xs={5}>
|
||||
<FBSelect
|
||||
key={props.apiFirmwareValue}
|
||||
list={FIRMWARE_CHOICES}
|
||||
selectedItem={FIRMWARE_CHOICES_DDI[props.apiFirmwareValue || "arduino"]}
|
||||
onChange={changeFirmwareHardware(props.dispatch)} />
|
||||
</Col>
|
||||
<Col xs={3}>
|
||||
<FlashFirmwareBtn
|
||||
apiFirmwareValue={props.apiFirmwareValue}
|
||||
botOnline={true} />
|
||||
</Col>
|
||||
</Row>
|
||||
</AlertCardTemplate>;
|
||||
|
||||
const SEED_DATA_OPTIONS: DropDownItem[] = [
|
||||
|
@ -107,7 +169,8 @@ class SeedDataMissing
|
|||
message={t(Content.SEED_DATA_SELECTION)}
|
||||
timeSettings={this.props.timeSettings}
|
||||
dispatch={this.props.dispatch}
|
||||
findApiAlertById={this.props.findApiAlertById}>
|
||||
findApiAlertById={this.props.findApiAlertById}
|
||||
iconName={"check-square"}>
|
||||
<Row>
|
||||
<Col xs={4}>
|
||||
<label>{t("Choose your FarmBot")}</label>
|
||||
|
@ -132,7 +195,8 @@ const TourNotTaken = (props: TourNotTakenProps) =>
|
|||
message={t(Content.TAKE_A_TOUR)}
|
||||
timeSettings={props.timeSettings}
|
||||
dispatch={props.dispatch}
|
||||
findApiAlertById={props.findApiAlertById}>
|
||||
findApiAlertById={props.findApiAlertById}
|
||||
iconName={"info-circle"}>
|
||||
<p>{t("Choose a tour to begin")}:</p>
|
||||
<TourList dispatch={props.dispatch} />
|
||||
</AlertCardTemplate>;
|
||||
|
@ -145,7 +209,8 @@ const UserNotWelcomed = (props: CommonAlertCardProps) =>
|
|||
message={t(Content.WELCOME)}
|
||||
timeSettings={props.timeSettings}
|
||||
dispatch={props.dispatch}
|
||||
findApiAlertById={props.findApiAlertById}>
|
||||
findApiAlertById={props.findApiAlertById}
|
||||
iconName={"info-circle"}>
|
||||
<p>
|
||||
{t("You're currently viewing the")} <b>{t("Message Center")}</b>.
|
||||
{t(Content.MESSAGE_CENTER_WELCOME)}
|
||||
|
@ -163,7 +228,8 @@ const DocumentationUnread = (props: CommonAlertCardProps) =>
|
|||
message={t(Content.READ_THE_DOCS)}
|
||||
timeSettings={props.timeSettings}
|
||||
dispatch={props.dispatch}
|
||||
findApiAlertById={props.findApiAlertById}>
|
||||
findApiAlertById={props.findApiAlertById}
|
||||
iconName={"info-circle"}>
|
||||
<p>
|
||||
{t("Head over to")}
|
||||
<a href={docLink()} target="_blank"
|
||||
|
|
|
@ -58,6 +58,7 @@ export interface AlertCardTemplateProps {
|
|||
children?: React.ReactNode;
|
||||
findApiAlertById?(id: number): UUID;
|
||||
dispatch?: Function;
|
||||
iconName?: string;
|
||||
}
|
||||
|
||||
export interface DismissAlertProps {
|
||||
|
|
Loading…
Reference in New Issue