remove old tools page
parent
9bd98aca1e
commit
90ddd78bb8
|
@ -30,7 +30,6 @@ import "../regimens/editor/interfaces";
|
||||||
import "../regimens/interfaces";
|
import "../regimens/interfaces";
|
||||||
import "../resources/interfaces";
|
import "../resources/interfaces";
|
||||||
import "../sequences/interfaces";
|
import "../sequences/interfaces";
|
||||||
import "../tools/interfaces";
|
|
||||||
|
|
||||||
describe("interfaces", () => {
|
describe("interfaces", () => {
|
||||||
it("cant explain why coverage is 0 for interface files", () => {
|
it("cant explain why coverage is 0 for interface files", () => {
|
||||||
|
|
|
@ -1,18 +1,15 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { t } from "../../i18next_wrapper";
|
import { t } from "../../i18next_wrapper";
|
||||||
import { Xyz, TaggedTool, TaggedToolSlotPointer } from "farmbot";
|
import { Xyz, TaggedTool, TaggedToolSlotPointer } from "farmbot";
|
||||||
import { Row, Col, BlurableInput, FBSelect, NULL_CHOICE, DropDownItem } from "../../ui";
|
|
||||||
import {
|
import {
|
||||||
directionIconClass, positionButtonTitle, newSlotDirection, positionIsDefined
|
Row, Col, BlurableInput, FBSelect, NULL_CHOICE, DropDownItem
|
||||||
} from "../../tools/components/toolbay_slot_menu";
|
} from "../../ui";
|
||||||
import {
|
|
||||||
DIRECTION_CHOICES, DIRECTION_CHOICES_DDI
|
|
||||||
} from "../../tools/components/toolbay_slot_direction_selection";
|
|
||||||
import { BotPosition } from "../../devices/interfaces";
|
import { BotPosition } from "../../devices/interfaces";
|
||||||
import { ToolPulloutDirection } from "farmbot/dist/resources/api_resources";
|
import { ToolPulloutDirection } from "farmbot/dist/resources/api_resources";
|
||||||
import { Popover } from "@blueprintjs/core";
|
import { Popover } from "@blueprintjs/core";
|
||||||
import { ToolSlotSVG } from "../map/layers/tool_slots/tool_graphics";
|
import { ToolSlotSVG } from "../map/layers/tool_slots/tool_graphics";
|
||||||
import { BotOriginQuadrant } from "../interfaces";
|
import { BotOriginQuadrant } from "../interfaces";
|
||||||
|
import { isNumber } from "lodash";
|
||||||
|
|
||||||
export interface GantryMountedInputProps {
|
export interface GantryMountedInputProps {
|
||||||
gantryMounted: boolean;
|
gantryMounted: boolean;
|
||||||
|
@ -189,3 +186,46 @@ export const SlotEditRows = (props: SlotEditRowsProps) =>
|
||||||
gantryMounted={props.toolSlot.body.gantry_mounted}
|
gantryMounted={props.toolSlot.body.gantry_mounted}
|
||||||
onChange={props.updateToolSlot} />}
|
onChange={props.updateToolSlot} />}
|
||||||
</div>;
|
</div>;
|
||||||
|
|
||||||
|
const directionIconClass = (slotDirection: ToolPulloutDirection) => {
|
||||||
|
switch (slotDirection) {
|
||||||
|
case ToolPulloutDirection.POSITIVE_X: return "fa fa-arrow-circle-right";
|
||||||
|
case ToolPulloutDirection.NEGATIVE_X: return "fa fa-arrow-circle-left";
|
||||||
|
case ToolPulloutDirection.POSITIVE_Y: return "fa fa-arrow-circle-up";
|
||||||
|
case ToolPulloutDirection.NEGATIVE_Y: return "fa fa-arrow-circle-down";
|
||||||
|
case ToolPulloutDirection.NONE: return "fa fa-dot-circle-o";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const positionButtonTitle = (position: BotPosition): string =>
|
||||||
|
positionIsDefined(position)
|
||||||
|
? `(${position.x}, ${position.y}, ${position.z})`
|
||||||
|
: t("(unknown)");
|
||||||
|
|
||||||
|
export const newSlotDirection =
|
||||||
|
(old: ToolPulloutDirection | undefined): ToolPulloutDirection =>
|
||||||
|
isNumber(old) && old < 4 ? old + 1 : ToolPulloutDirection.NONE;
|
||||||
|
|
||||||
|
export const positionIsDefined = (position: BotPosition): boolean =>
|
||||||
|
isNumber(position.x) && isNumber(position.y) && isNumber(position.z);
|
||||||
|
|
||||||
|
export const DIRECTION_CHOICES_DDI: { [index: number]: DropDownItem } = {
|
||||||
|
[ToolPulloutDirection.NONE]:
|
||||||
|
{ label: t("None"), value: ToolPulloutDirection.NONE },
|
||||||
|
[ToolPulloutDirection.POSITIVE_X]:
|
||||||
|
{ label: t("Positive X"), value: ToolPulloutDirection.POSITIVE_X },
|
||||||
|
[ToolPulloutDirection.NEGATIVE_X]:
|
||||||
|
{ label: t("Negative X"), value: ToolPulloutDirection.NEGATIVE_X },
|
||||||
|
[ToolPulloutDirection.POSITIVE_Y]:
|
||||||
|
{ label: t("Positive Y"), value: ToolPulloutDirection.POSITIVE_Y },
|
||||||
|
[ToolPulloutDirection.NEGATIVE_Y]:
|
||||||
|
{ label: t("Negative Y"), value: ToolPulloutDirection.NEGATIVE_Y },
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DIRECTION_CHOICES: DropDownItem[] = [
|
||||||
|
DIRECTION_CHOICES_DDI[ToolPulloutDirection.NONE],
|
||||||
|
DIRECTION_CHOICES_DDI[ToolPulloutDirection.POSITIVE_X],
|
||||||
|
DIRECTION_CHOICES_DDI[ToolPulloutDirection.NEGATIVE_X],
|
||||||
|
DIRECTION_CHOICES_DDI[ToolPulloutDirection.POSITIVE_Y],
|
||||||
|
DIRECTION_CHOICES_DDI[ToolPulloutDirection.NEGATIVE_Y],
|
||||||
|
];
|
||||||
|
|
|
@ -149,12 +149,6 @@ export const UNBOUND_ROUTES = [
|
||||||
getModule: () => import("./sequences/sequences"),
|
getModule: () => import("./sequences/sequences"),
|
||||||
key: "Sequences",
|
key: "Sequences",
|
||||||
}),
|
}),
|
||||||
route({
|
|
||||||
children: false,
|
|
||||||
$: "/tools",
|
|
||||||
getModule: () => import("./tools"),
|
|
||||||
key: "Tools",
|
|
||||||
}),
|
|
||||||
route({
|
route({
|
||||||
children: false,
|
children: false,
|
||||||
$: "/designer",
|
$: "/designer",
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import { mount, shallow } from "enzyme";
|
|
||||||
import { RawTools as Tools } from "../index";
|
|
||||||
import { Props } from "../interfaces";
|
|
||||||
import {
|
|
||||||
fakeToolSlot, fakeTool
|
|
||||||
} from "../../__test_support__/fake_state/resources";
|
|
||||||
|
|
||||||
describe("<Tools />", () => {
|
|
||||||
const fakeProps = (): Props => ({
|
|
||||||
toolSlots: [],
|
|
||||||
tools: [fakeTool()],
|
|
||||||
getToolSlots: () => [fakeToolSlot()],
|
|
||||||
getToolOptions: () => [],
|
|
||||||
getChosenToolOption: () => ({ label: "None", value: "" }),
|
|
||||||
getToolByToolSlotUUID: fakeTool,
|
|
||||||
changeToolSlot: jest.fn(),
|
|
||||||
isActive: () => true,
|
|
||||||
dispatch: jest.fn(),
|
|
||||||
botPosition: { x: undefined, y: undefined, z: undefined }
|
|
||||||
});
|
|
||||||
|
|
||||||
it("renders", () => {
|
|
||||||
const wrapper = mount(<Tools {...fakeProps()} />);
|
|
||||||
const txt = wrapper.text();
|
|
||||||
const strings = [
|
|
||||||
"Tool Slots",
|
|
||||||
"SlotXYZ",
|
|
||||||
"Tool or Seed Container",
|
|
||||||
"Tools",
|
|
||||||
"NameStatus",
|
|
||||||
"Fooactive"];
|
|
||||||
strings.map(string => expect(txt).toContain(string));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("shows forms", () => {
|
|
||||||
const wrapper = shallow(<Tools {...fakeProps()} />);
|
|
||||||
expect(wrapper.find("ToolList").length).toEqual(1);
|
|
||||||
expect(wrapper.find("ToolBayList").length).toEqual(1);
|
|
||||||
expect(wrapper.find("ToolForm").length).toEqual(0);
|
|
||||||
expect(wrapper.find("ToolBayForm").length).toEqual(0);
|
|
||||||
wrapper.setState({ editingBays: true, editingTools: true });
|
|
||||||
expect(wrapper.find("ToolList").length).toEqual(0);
|
|
||||||
expect(wrapper.find("ToolBayList").length).toEqual(0);
|
|
||||||
expect(wrapper.find("ToolForm").length).toEqual(1);
|
|
||||||
expect(wrapper.find("ToolBayForm").length).toEqual(1);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,30 +0,0 @@
|
||||||
jest.mock("../../api/crud", () => ({ edit: jest.fn() }));
|
|
||||||
|
|
||||||
import { mapStateToProps } from "../state_to_props";
|
|
||||||
import { fakeState } from "../../__test_support__/fake_state";
|
|
||||||
import { NULL_CHOICE } from "../../ui";
|
|
||||||
import { fakeToolSlot } from "../../__test_support__/fake_state/resources";
|
|
||||||
import { edit } from "../../api/crud";
|
|
||||||
|
|
||||||
describe("mapStateToProps()", () => {
|
|
||||||
it("getChosenToolOption()", () => {
|
|
||||||
const props = mapStateToProps(fakeState());
|
|
||||||
const result = props.getChosenToolOption(undefined);
|
|
||||||
expect(result).toEqual(NULL_CHOICE);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("changeToolSlot(): no tool_id", () => {
|
|
||||||
const props = mapStateToProps(fakeState());
|
|
||||||
const tool = fakeToolSlot();
|
|
||||||
props.changeToolSlot(tool, jest.fn())({ label: "", value: "" });
|
|
||||||
// tslint:disable-next-line:no-null-keyword
|
|
||||||
expect(edit).toHaveBeenCalledWith(tool, { tool_id: null });
|
|
||||||
});
|
|
||||||
|
|
||||||
it("changeToolSlot(): tool_id", () => {
|
|
||||||
const props = mapStateToProps(fakeState());
|
|
||||||
const tool = fakeToolSlot();
|
|
||||||
props.changeToolSlot(tool, jest.fn())({ label: "", value: 1 });
|
|
||||||
expect(edit).toHaveBeenCalledWith(tool, { tool_id: 1 });
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,91 +0,0 @@
|
||||||
jest.mock("../../../api/crud", () => ({
|
|
||||||
init: jest.fn(),
|
|
||||||
saveAll: jest.fn(),
|
|
||||||
destroy: jest.fn(),
|
|
||||||
edit: jest.fn(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
import { SpecialStatus } from "farmbot";
|
|
||||||
jest.mock("../../../resources/tagged_resources", () => ({
|
|
||||||
getArrayStatus: () => SpecialStatus.SAVED,
|
|
||||||
isTaggedResource: () => true
|
|
||||||
}));
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
import { ToolForm } from "../tool_form";
|
|
||||||
import { mount, shallow } from "enzyme";
|
|
||||||
import { fakeTool } from "../../../__test_support__/fake_state/resources";
|
|
||||||
import { ToolListAndFormProps } from "../../interfaces";
|
|
||||||
import { clickButton } from "../../../__test_support__/helpers";
|
|
||||||
import { init, saveAll, destroy, edit } from "../../../api/crud";
|
|
||||||
|
|
||||||
describe("<ToolForm/>", () => {
|
|
||||||
function fakeProps(): ToolListAndFormProps {
|
|
||||||
return {
|
|
||||||
dispatch: jest.fn(),
|
|
||||||
toggle: jest.fn(),
|
|
||||||
tools: [fakeTool(), fakeTool()],
|
|
||||||
isActive: jest.fn(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
it("renders", () => {
|
|
||||||
const p = fakeProps();
|
|
||||||
const wrapper = mount(<ToolForm {...p} />);
|
|
||||||
expect(wrapper.find("input").length).toEqual(p.tools.length);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("saves tools", () => {
|
|
||||||
const wrapper = mount(<ToolForm {...fakeProps()} />);
|
|
||||||
clickButton(wrapper, 1, "saved", { partial_match: true });
|
|
||||||
expect(saveAll).toHaveBeenCalledTimes(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("adds new tool", () => {
|
|
||||||
const wrapper = mount(<ToolForm {...fakeProps()} />);
|
|
||||||
expect(wrapper.props().tools.length).toEqual(2);
|
|
||||||
clickButton(wrapper, 2, "");
|
|
||||||
expect(init).toHaveBeenCalledWith("Tool", { name: "Tool 3" });
|
|
||||||
});
|
|
||||||
|
|
||||||
it("adds stock tools", () => {
|
|
||||||
const wrapper = mount(<ToolForm {...fakeProps()} />);
|
|
||||||
clickButton(wrapper, 3, "stock tools");
|
|
||||||
expect(init).toHaveBeenCalledTimes(6);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("changes tool name", () => {
|
|
||||||
const p = fakeProps();
|
|
||||||
const wrapper = shallow(<ToolForm {...p} />);
|
|
||||||
wrapper.find("BlurableInput").first().simulate("commit", {
|
|
||||||
currentTarget: { value: "New Tool Name" }
|
|
||||||
});
|
|
||||||
expect(edit).toHaveBeenCalledWith(p.tools[0], { name: "New Tool Name" });
|
|
||||||
});
|
|
||||||
|
|
||||||
it("has red delete button", () => {
|
|
||||||
const p = fakeProps();
|
|
||||||
p.isActive = () => false;
|
|
||||||
const wrapper = mount(<ToolForm {...p} />);
|
|
||||||
const delBtn = wrapper.find("button").last();
|
|
||||||
expect(delBtn.hasClass("red")).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("deletes tool", () => {
|
|
||||||
const p = fakeProps();
|
|
||||||
p.isActive = () => false;
|
|
||||||
const wrapper = mount(<ToolForm {...p} />);
|
|
||||||
const delBtn = wrapper.find("button").last();
|
|
||||||
delBtn.simulate("click");
|
|
||||||
expect(destroy).toHaveBeenCalledWith(p.tools[1].uuid);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("has gray delete button", () => {
|
|
||||||
const p = fakeProps();
|
|
||||||
p.isActive = () => true;
|
|
||||||
const wrapper = mount(<ToolForm {...p} />);
|
|
||||||
const delBtn = wrapper.find("button").last();
|
|
||||||
expect(delBtn.hasClass("pseudo-disabled")).toBeTruthy();
|
|
||||||
expect(delBtn.props().title).toContain("in slot");
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,24 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import { ToolList } from "../tool_list";
|
|
||||||
import { mount } from "enzyme";
|
|
||||||
import { mapStateToProps } from "../../state_to_props";
|
|
||||||
import { fakeState } from "../../../__test_support__/fake_state";
|
|
||||||
import { ToolListAndFormProps } from "../../interfaces";
|
|
||||||
|
|
||||||
describe("<ToolList />", () => {
|
|
||||||
const fakeProps = (): ToolListAndFormProps => {
|
|
||||||
const props = mapStateToProps(fakeState());
|
|
||||||
return {
|
|
||||||
dispatch: jest.fn(),
|
|
||||||
tools: props.tools,
|
|
||||||
toggle: jest.fn(),
|
|
||||||
isActive: props.isActive,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
it("renders tool names and statuses", () => {
|
|
||||||
const wrapper = mount(<ToolList {...fakeProps()} />);
|
|
||||||
expect(wrapper.text()).toContain("Trench Digging Toolactive");
|
|
||||||
expect(wrapper.text()).toContain("Berry Picking Toolinactive");
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,26 +0,0 @@
|
||||||
jest.mock("../../../api/crud", () => ({ destroy: jest.fn() }));
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
import { ToolSlotRowProps, ToolSlotRow } from "../tool_slot_row";
|
|
||||||
import { mount } from "enzyme";
|
|
||||||
import { destroy } from "../../../api/crud";
|
|
||||||
import { fakeToolSlot } from "../../../__test_support__/fake_state/resources";
|
|
||||||
|
|
||||||
describe("<ToolSlotRow />", () => {
|
|
||||||
const fakeProps = (): ToolSlotRowProps => ({
|
|
||||||
dispatch: jest.fn(),
|
|
||||||
slot: fakeToolSlot(),
|
|
||||||
botPosition: { x: undefined, y: undefined, z: undefined },
|
|
||||||
toolOptions: [],
|
|
||||||
chosenToolOption: { label: "", value: "" },
|
|
||||||
onToolSlotChange: jest.fn(),
|
|
||||||
gantryMounted: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
it("deletes slot", () => {
|
|
||||||
const p = fakeProps();
|
|
||||||
const wrapper = mount(<ToolSlotRow {...p} />);
|
|
||||||
wrapper.find("button").last().simulate("click");
|
|
||||||
expect(destroy).toHaveBeenCalledWith(p.slot.uuid);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,50 +0,0 @@
|
||||||
jest.mock("../../../api/crud", () => ({
|
|
||||||
init: jest.fn(),
|
|
||||||
saveAll: jest.fn(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
import { ToolBayForm } from "../toolbay_form";
|
|
||||||
import { mount } from "enzyme";
|
|
||||||
import { mapStateToProps } from "../../state_to_props";
|
|
||||||
import { fakeState } from "../../../__test_support__/fake_state";
|
|
||||||
import { ToolBayFormProps } from "../../interfaces";
|
|
||||||
import { clickButton } from "../../../__test_support__/helpers";
|
|
||||||
import { saveAll, init } from "../../../api/crud";
|
|
||||||
import { emptyToolSlotBody } from "../empty_tool_slot";
|
|
||||||
|
|
||||||
describe("<ToolBayForm/>", () => {
|
|
||||||
const fakeProps = (): ToolBayFormProps => {
|
|
||||||
const props = mapStateToProps(fakeState());
|
|
||||||
return {
|
|
||||||
toggle: jest.fn(),
|
|
||||||
dispatch: jest.fn(),
|
|
||||||
toolSlots: props.toolSlots,
|
|
||||||
getToolSlots: props.getToolSlots,
|
|
||||||
getChosenToolOption: props.getChosenToolOption,
|
|
||||||
getToolOptions: props.getToolOptions,
|
|
||||||
changeToolSlot: props.changeToolSlot,
|
|
||||||
botPosition: { x: 1, y: 2, z: 3 },
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
it("renders ToolSlot", () => {
|
|
||||||
const wrapper = mount(<ToolBayForm {...fakeProps()} />);
|
|
||||||
const inputs = wrapper.find("input");
|
|
||||||
expect(inputs.length).toEqual(3);
|
|
||||||
expect(wrapper.text()).toContain("Trench Digging Tool");
|
|
||||||
[0, 1, 2].map(i => expect(inputs.at(i).props().value).toEqual("10"));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("saves tool slots", () => {
|
|
||||||
const wrapper = mount(<ToolBayForm {...fakeProps()} />);
|
|
||||||
clickButton(wrapper, 1, "saved", { partial_match: true });
|
|
||||||
expect(saveAll).toHaveBeenCalledTimes(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("adds new tool slot", () => {
|
|
||||||
const wrapper = mount(<ToolBayForm {...fakeProps()} />);
|
|
||||||
clickButton(wrapper, 2, "");
|
|
||||||
expect(init).toHaveBeenCalledWith("Point", emptyToolSlotBody());
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,10 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import { ToolBayHeader } from "../toolbay_header";
|
|
||||||
import { mount } from "enzyme";
|
|
||||||
|
|
||||||
describe("<ToolBayHeader />", () => {
|
|
||||||
it("renders", () => {
|
|
||||||
const header = mount(<ToolBayHeader />);
|
|
||||||
expect(header.text()).toEqual("SlotXYZTool or Seed Container");
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,31 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import { ToolBayList } from "../toolbay_list";
|
|
||||||
import { mount } from "enzyme";
|
|
||||||
import { mapStateToProps } from "../../state_to_props";
|
|
||||||
import { fakeState } from "../../../__test_support__/fake_state";
|
|
||||||
import { ToolBayListProps } from "../../interfaces";
|
|
||||||
|
|
||||||
describe("<ToolBayList />", () => {
|
|
||||||
const fakeProps = (): ToolBayListProps => {
|
|
||||||
const props = mapStateToProps(fakeState());
|
|
||||||
return {
|
|
||||||
getToolByToolSlotUUID: props.getToolByToolSlotUUID,
|
|
||||||
getToolSlots: props.getToolSlots,
|
|
||||||
toggle: jest.fn(),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
it("renders", () => {
|
|
||||||
const wrapper = mount(<ToolBayList {...fakeProps()} />);
|
|
||||||
expect(wrapper.text()).toContain("1101010Trench Digging Tool");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("renders gantry mounted slot", () => {
|
|
||||||
const p = fakeProps();
|
|
||||||
const slots = p.getToolSlots();
|
|
||||||
slots[0].body.gantry_mounted = true;
|
|
||||||
p.getToolSlots = () => slots;
|
|
||||||
const wrapper = mount(<ToolBayList {...fakeProps()} />);
|
|
||||||
expect(wrapper.text()).toContain("1Gantry1010Trench Digging Tool");
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,25 +0,0 @@
|
||||||
jest.mock("../../../api/crud", () => ({ edit: jest.fn() }));
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
import { shallow } from "enzyme";
|
|
||||||
import { ToolBayNumberCol, TBNumColProps } from "../toolbay_number_column";
|
|
||||||
import { edit } from "../../../api/crud";
|
|
||||||
import { fakeToolSlot } from "../../../__test_support__/fake_state/resources";
|
|
||||||
|
|
||||||
describe("<ToolBayNumberCol />", () => {
|
|
||||||
const fakeProps = (): TBNumColProps => ({
|
|
||||||
axis: "x",
|
|
||||||
value: 0,
|
|
||||||
dispatch: jest.fn(),
|
|
||||||
slot: fakeToolSlot(),
|
|
||||||
});
|
|
||||||
|
|
||||||
it("edits value", () => {
|
|
||||||
const p = fakeProps();
|
|
||||||
const wrapper = shallow(<ToolBayNumberCol {...p} />);
|
|
||||||
wrapper.find("BlurableInput").simulate("commit", {
|
|
||||||
currentTarget: { value: "1.23" }
|
|
||||||
});
|
|
||||||
expect(edit).toHaveBeenCalledWith(p.slot, { x: 1.23 });
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,31 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import { shallow } from "enzyme";
|
|
||||||
import {
|
|
||||||
SlotDirectionSelect, SlotDirectionSelectProps
|
|
||||||
} from "../toolbay_slot_direction_selection";
|
|
||||||
import { fakeToolSlot } from "../../../__test_support__/fake_state/resources";
|
|
||||||
import { Actions } from "../../../constants";
|
|
||||||
import { SpecialStatus } from "farmbot";
|
|
||||||
|
|
||||||
describe("<SlotDirectionSelect />", () => {
|
|
||||||
const fakeProps = (): SlotDirectionSelectProps => {
|
|
||||||
return {
|
|
||||||
dispatch: jest.fn(),
|
|
||||||
slot: fakeToolSlot()
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
it("changes slot direction", () => {
|
|
||||||
const p = fakeProps();
|
|
||||||
const wrapper = shallow(<SlotDirectionSelect {...p} />);
|
|
||||||
wrapper.simulate("change", { value: 1 });
|
|
||||||
expect(p.dispatch).toHaveBeenCalledWith({
|
|
||||||
payload: {
|
|
||||||
specialStatus: SpecialStatus.DIRTY,
|
|
||||||
update: { pullout_direction: 1 },
|
|
||||||
uuid: expect.any(String)
|
|
||||||
},
|
|
||||||
type: Actions.EDIT_RESOURCE
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,95 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import { mount } from "enzyme";
|
|
||||||
import { SlotMenu, SlotMenuProps } from "../toolbay_slot_menu";
|
|
||||||
import { fakeToolSlot } from "../../../__test_support__/fake_state/resources";
|
|
||||||
import { Actions } from "../../../constants";
|
|
||||||
import { SpecialStatus } from "farmbot";
|
|
||||||
|
|
||||||
describe("<SlotMenu />", () => {
|
|
||||||
const fakeProps = (): SlotMenuProps => {
|
|
||||||
return {
|
|
||||||
dispatch: jest.fn(),
|
|
||||||
slot: fakeToolSlot(),
|
|
||||||
botPosition: { x: 1, y: 2, z: 3 }
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
it("changes slot direction", () => {
|
|
||||||
const p = fakeProps();
|
|
||||||
const wrapper = mount(<SlotMenu {...p} />);
|
|
||||||
wrapper.find("i").first().simulate("click");
|
|
||||||
expect(p.dispatch).toHaveBeenCalledWith({
|
|
||||||
payload: {
|
|
||||||
specialStatus: SpecialStatus.DIRTY,
|
|
||||||
update: { pullout_direction: 1 },
|
|
||||||
uuid: expect.any(String)
|
|
||||||
},
|
|
||||||
type: Actions.EDIT_RESOURCE
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("changes slot direction: reset", () => {
|
|
||||||
const p = fakeProps();
|
|
||||||
p.slot.body.pullout_direction = 4;
|
|
||||||
const wrapper = mount(<SlotMenu {...p} />);
|
|
||||||
wrapper.find("i").first().simulate("click");
|
|
||||||
expect(p.dispatch).toHaveBeenCalledWith({
|
|
||||||
payload: {
|
|
||||||
specialStatus: SpecialStatus.DIRTY,
|
|
||||||
update: { pullout_direction: 0 },
|
|
||||||
uuid: expect.any(String)
|
|
||||||
},
|
|
||||||
type: Actions.EDIT_RESOURCE
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const checkDirection = (direction: number, expected: string) => {
|
|
||||||
it("icon shows direction", () => {
|
|
||||||
const p = fakeProps();
|
|
||||||
p.slot.body.pullout_direction = direction;
|
|
||||||
const wrapper = mount(<SlotMenu {...p} />);
|
|
||||||
expect(wrapper.html()).toContain(expected);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
checkDirection(1, "right");
|
|
||||||
checkDirection(2, "left");
|
|
||||||
checkDirection(3, "up");
|
|
||||||
checkDirection(4, "down");
|
|
||||||
|
|
||||||
it("fills inputs with bot position", () => {
|
|
||||||
const p = fakeProps();
|
|
||||||
const wrapper = mount(<SlotMenu {...p} />);
|
|
||||||
const buttons = wrapper.find("button");
|
|
||||||
buttons.last().simulate("click");
|
|
||||||
expect(p.dispatch).toHaveBeenCalledWith({
|
|
||||||
type: Actions.EDIT_RESOURCE,
|
|
||||||
payload: expect.objectContaining({
|
|
||||||
update: { x: 1, y: 2, z: 3 }
|
|
||||||
})
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("doesn't fills inputs with bot position unknown", () => {
|
|
||||||
const p = fakeProps();
|
|
||||||
p.botPosition = { x: undefined, y: undefined, z: undefined };
|
|
||||||
const wrapper = mount(<SlotMenu {...p} />);
|
|
||||||
const buttons = wrapper.find("button");
|
|
||||||
buttons.last().simulate("click");
|
|
||||||
expect(p.dispatch).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("sets gantry_mounted", () => {
|
|
||||||
const p = fakeProps();
|
|
||||||
p.slot.body.gantry_mounted = false;
|
|
||||||
const wrapper = mount(<SlotMenu {...p} />);
|
|
||||||
wrapper.find("input").last().simulate("change");
|
|
||||||
expect(p.dispatch).toHaveBeenCalledWith({
|
|
||||||
payload: {
|
|
||||||
specialStatus: SpecialStatus.DIRTY,
|
|
||||||
update: { gantry_mounted: true },
|
|
||||||
uuid: expect.any(String)
|
|
||||||
},
|
|
||||||
type: Actions.EDIT_RESOURCE
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,14 +0,0 @@
|
||||||
import { ToolSlotPointer } from "farmbot/dist/resources/api_resources";
|
|
||||||
|
|
||||||
export const emptyToolSlotBody = (): ToolSlotPointer => ({
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
z: 0,
|
|
||||||
radius: 25,
|
|
||||||
pointer_type: "ToolSlot",
|
|
||||||
meta: {},
|
|
||||||
tool_id: undefined,
|
|
||||||
name: "Tool Slot",
|
|
||||||
pullout_direction: 0,
|
|
||||||
gantry_mounted: false,
|
|
||||||
});
|
|
|
@ -1,4 +0,0 @@
|
||||||
export * from "./toolbay_form";
|
|
||||||
export * from "./toolbay_list";
|
|
||||||
export * from "./tool_list";
|
|
||||||
export * from "./tool_form";
|
|
|
@ -1,98 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import { ToolListAndFormProps } from "../interfaces";
|
|
||||||
import {
|
|
||||||
Row,
|
|
||||||
Col,
|
|
||||||
Widget,
|
|
||||||
WidgetBody,
|
|
||||||
WidgetHeader,
|
|
||||||
BlurableInput,
|
|
||||||
SaveBtn
|
|
||||||
} from "../../ui";
|
|
||||||
import { getArrayStatus } from "../../resources/tagged_resources";
|
|
||||||
import { edit, destroy, init, saveAll } from "../../api/crud";
|
|
||||||
import { ToolTips } from "../../constants";
|
|
||||||
import { TaggedTool } from "farmbot";
|
|
||||||
import { t } from "../../i18next_wrapper";
|
|
||||||
|
|
||||||
export class ToolForm extends React.Component<ToolListAndFormProps, {}> {
|
|
||||||
get newToolName() { return t("Tool ") + (this.props.tools.length + 1); }
|
|
||||||
|
|
||||||
newTool = (name = this.newToolName) => {
|
|
||||||
this.props.dispatch(init("Tool", { name }));
|
|
||||||
};
|
|
||||||
|
|
||||||
stockTools = () => {
|
|
||||||
this.newTool(t("Seeder"));
|
|
||||||
this.newTool(t("Watering Nozzle"));
|
|
||||||
this.newTool(t("Weeder"));
|
|
||||||
this.newTool(t("Soil Sensor"));
|
|
||||||
this.newTool(t("Seed Bin"));
|
|
||||||
this.newTool(t("Seed Tray"));
|
|
||||||
}
|
|
||||||
|
|
||||||
HeaderButtons = () => {
|
|
||||||
const { dispatch, tools, toggle } = this.props;
|
|
||||||
const specialStatus = getArrayStatus(tools);
|
|
||||||
return <div>
|
|
||||||
<button
|
|
||||||
className="fb-button gray"
|
|
||||||
onClick={toggle}
|
|
||||||
disabled={!!specialStatus}>
|
|
||||||
{t("Back")}
|
|
||||||
</button>
|
|
||||||
<SaveBtn
|
|
||||||
status={specialStatus}
|
|
||||||
onClick={() => dispatch(saveAll(tools, toggle))} />
|
|
||||||
<button
|
|
||||||
className="fb-button green"
|
|
||||||
onClick={() => this.newTool()}>
|
|
||||||
<i className="fa fa-plus" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="fb-button green"
|
|
||||||
onClick={this.stockTools}>
|
|
||||||
<i className="fa fa-plus" style={{ marginRight: "0.5rem" }} />
|
|
||||||
{t("Stock Tools")}
|
|
||||||
</button>
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
ToolForm = (tool: TaggedTool, index: number) => {
|
|
||||||
const { dispatch, isActive } = this.props;
|
|
||||||
const inSlotClass = isActive(tool) ? "pseudo-disabled" : "";
|
|
||||||
return <Row key={index}>
|
|
||||||
<Col xs={10}>
|
|
||||||
<BlurableInput
|
|
||||||
id={(tool.body.id || "Error getting ID").toString()}
|
|
||||||
value={tool.body.name || "Error getting Name"}
|
|
||||||
onCommit={e =>
|
|
||||||
dispatch(edit(tool, { name: e.currentTarget.value }))} />
|
|
||||||
</Col>
|
|
||||||
<Col xs={2}>
|
|
||||||
<button
|
|
||||||
className={`fb-button red ${inSlotClass} del-button`}
|
|
||||||
title={isActive(tool) ? t("in slot") : t("Delete")}
|
|
||||||
onClick={() => dispatch(destroy(tool.uuid))}>
|
|
||||||
<i className="fa fa-times"></i>
|
|
||||||
</button>
|
|
||||||
</Col>
|
|
||||||
</Row>;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return <Widget className="tools-widget">
|
|
||||||
<WidgetHeader helpText={ToolTips.TOOL_LIST} title="Tools and Seed Containers">
|
|
||||||
<this.HeaderButtons />
|
|
||||||
</WidgetHeader>
|
|
||||||
<WidgetBody>
|
|
||||||
<Row>
|
|
||||||
<Col xs={12}>
|
|
||||||
<label>{t("Name")}</label>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
{this.props.tools.map(this.ToolForm)}
|
|
||||||
</WidgetBody>
|
|
||||||
</Widget>;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import { Row, Col, Widget, WidgetBody, WidgetHeader } from "../../ui";
|
|
||||||
import { ToolListAndFormProps } from "../interfaces";
|
|
||||||
import { TaggedTool } from "farmbot";
|
|
||||||
import { ToolTips } from "../../constants";
|
|
||||||
import { t } from "../../i18next_wrapper";
|
|
||||||
|
|
||||||
enum ColWidth {
|
|
||||||
toolName = 8,
|
|
||||||
status = 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ToolList extends React.Component<ToolListAndFormProps, {}> {
|
|
||||||
|
|
||||||
ToolListItem = (tool: TaggedTool) => {
|
|
||||||
return <Row key={tool.uuid}>
|
|
||||||
<Col xs={ColWidth.toolName}>
|
|
||||||
{tool.body.name || "Name not found"}
|
|
||||||
</Col>
|
|
||||||
<Col xs={ColWidth.status}>
|
|
||||||
{this.props.isActive(tool) ? t("active") : t("inactive")}
|
|
||||||
</Col>
|
|
||||||
</Row>;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { tools, toggle } = this.props;
|
|
||||||
|
|
||||||
return <Widget className="tool-list">
|
|
||||||
<WidgetHeader helpText={ToolTips.TOOL_LIST} title={t("Tools and Seed Containers")}>
|
|
||||||
<button
|
|
||||||
className="fb-button gray"
|
|
||||||
onClick={toggle}>
|
|
||||||
{t("Edit")}
|
|
||||||
</button>
|
|
||||||
</WidgetHeader>
|
|
||||||
<WidgetBody>
|
|
||||||
<Row>
|
|
||||||
<Col xs={ColWidth.toolName}>
|
|
||||||
<label>{t("Name")}</label>
|
|
||||||
</Col>
|
|
||||||
<Col xs={ColWidth.status}>
|
|
||||||
<label>{t("Status")}</label>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
{tools.map(this.ToolListItem)}
|
|
||||||
</WidgetBody>
|
|
||||||
</Widget>;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import { Row, Col, FBSelect, DropDownItem } from "../../ui";
|
|
||||||
import { Popover, Position } from "@blueprintjs/core";
|
|
||||||
import { SlotMenu } from "./toolbay_slot_menu";
|
|
||||||
import { TaggedToolSlotPointer } from "farmbot";
|
|
||||||
import { destroy } from "../../api/crud";
|
|
||||||
import { Xyz } from "../../devices/interfaces";
|
|
||||||
import { ToolBayNumberCol } from "./toolbay_number_column";
|
|
||||||
import { t } from "../../i18next_wrapper";
|
|
||||||
|
|
||||||
export interface ToolSlotRowProps {
|
|
||||||
dispatch: Function;
|
|
||||||
slot: TaggedToolSlotPointer;
|
|
||||||
botPosition: Record<"x" | "y" | "z", number | undefined>;
|
|
||||||
/** List of all legal tool options for the current tool slot. */
|
|
||||||
toolOptions: DropDownItem[];
|
|
||||||
/** The current tool (if any) in the slot. */
|
|
||||||
chosenToolOption: DropDownItem;
|
|
||||||
/** Broadcast tool change back up to parent. */
|
|
||||||
onToolSlotChange(item: DropDownItem): void;
|
|
||||||
/** Gantry-mounted tool slot. */
|
|
||||||
gantryMounted: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
type Axis = Xyz & keyof (TaggedToolSlotPointer["body"]);
|
|
||||||
const axes: Axis[] = ["x", "y", "z"];
|
|
||||||
|
|
||||||
export function ToolSlotRow(props: ToolSlotRowProps) {
|
|
||||||
const { dispatch, slot, botPosition, toolOptions, onToolSlotChange,
|
|
||||||
chosenToolOption, gantryMounted } = props;
|
|
||||||
|
|
||||||
return <Row>
|
|
||||||
<Col xs={1}>
|
|
||||||
<Popover position={Position.BOTTOM_LEFT}>
|
|
||||||
<i className="fa fa-gear" />
|
|
||||||
<SlotMenu
|
|
||||||
dispatch={dispatch}
|
|
||||||
slot={slot}
|
|
||||||
botPosition={botPosition} />
|
|
||||||
</Popover>
|
|
||||||
</Col>
|
|
||||||
{axes
|
|
||||||
.map(axis => ({ axis, dispatch, slot, value: (slot.body[axis] || 0) }))
|
|
||||||
.map(axisProps => (axisProps.axis === "x" && gantryMounted)
|
|
||||||
? <Col xs={2} key={slot.uuid + axisProps.axis}>
|
|
||||||
<input disabled value={t("Gantry")} />
|
|
||||||
</Col>
|
|
||||||
: <ToolBayNumberCol key={slot.uuid + axisProps.axis} {...axisProps} />)}
|
|
||||||
<Col xs={4}>
|
|
||||||
<FBSelect
|
|
||||||
list={toolOptions}
|
|
||||||
selectedItem={chosenToolOption}
|
|
||||||
allowEmpty={true}
|
|
||||||
onChange={onToolSlotChange} />
|
|
||||||
</Col>
|
|
||||||
<Col xs={1}>
|
|
||||||
<button
|
|
||||||
className="red fb-button del-button"
|
|
||||||
title={t("Delete")}
|
|
||||||
onClick={() => dispatch(destroy(slot.uuid))}>
|
|
||||||
<i className="fa fa-times" />
|
|
||||||
</button>
|
|
||||||
</Col>
|
|
||||||
</Row>;
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import { ToolBayFormProps } from "../interfaces";
|
|
||||||
import {
|
|
||||||
Widget,
|
|
||||||
WidgetBody,
|
|
||||||
WidgetHeader,
|
|
||||||
SaveBtn,
|
|
||||||
} from "../../ui";
|
|
||||||
|
|
||||||
import {
|
|
||||||
getArrayStatus
|
|
||||||
} from "../../resources/tagged_resources";
|
|
||||||
import { saveAll, init } from "../../api/crud";
|
|
||||||
import { ToolBayHeader } from "./toolbay_header";
|
|
||||||
import { ToolTips } from "../../constants";
|
|
||||||
import { ToolSlotRow } from "./tool_slot_row";
|
|
||||||
import { emptyToolSlotBody } from "./empty_tool_slot";
|
|
||||||
import { TaggedToolSlotPointer } from "farmbot";
|
|
||||||
import { t } from "../../i18next_wrapper";
|
|
||||||
|
|
||||||
export class ToolBayForm extends React.Component<ToolBayFormProps, {}> {
|
|
||||||
|
|
||||||
HeaderButtons = () => {
|
|
||||||
const { toggle, dispatch, toolSlots } = this.props;
|
|
||||||
const toolSlotStatus = getArrayStatus(toolSlots);
|
|
||||||
return <div>
|
|
||||||
<button
|
|
||||||
className="gray fb-button"
|
|
||||||
disabled={!!toolSlotStatus}
|
|
||||||
onClick={toggle}>
|
|
||||||
{t("Back")}
|
|
||||||
</button>
|
|
||||||
<SaveBtn
|
|
||||||
status={toolSlotStatus}
|
|
||||||
onClick={() => dispatch(saveAll(toolSlots, toggle))} />
|
|
||||||
<button
|
|
||||||
className="green fb-button"
|
|
||||||
onClick={() => dispatch(init("Point", emptyToolSlotBody()))}>
|
|
||||||
<i className="fa fa-plus" />
|
|
||||||
</button>
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
ToolbayForm = (slot: TaggedToolSlotPointer) => {
|
|
||||||
const { dispatch, botPosition } = this.props;
|
|
||||||
return <ToolSlotRow
|
|
||||||
key={slot.uuid}
|
|
||||||
dispatch={dispatch}
|
|
||||||
slot={slot}
|
|
||||||
botPosition={botPosition}
|
|
||||||
toolOptions={this.props.getToolOptions()}
|
|
||||||
gantryMounted={slot.body.gantry_mounted}
|
|
||||||
onToolSlotChange={this.props.changeToolSlot(slot, this.props.dispatch)}
|
|
||||||
chosenToolOption={this.props.getChosenToolOption(slot.uuid)} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return <div className={"toolbay-widget"}>
|
|
||||||
<Widget>
|
|
||||||
<WidgetHeader helpText={ToolTips.TOOLBAY_LIST} title={t("Tool Slots")}>
|
|
||||||
<this.HeaderButtons />
|
|
||||||
</WidgetHeader>
|
|
||||||
<WidgetBody>
|
|
||||||
<ToolBayHeader />
|
|
||||||
{this.props.getToolSlots().map(this.ToolbayForm)}
|
|
||||||
</WidgetBody>
|
|
||||||
</Widget>
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import { Col, Row } from "../../ui";
|
|
||||||
import { t } from "../../i18next_wrapper";
|
|
||||||
|
|
||||||
export function ToolBayHeader() {
|
|
||||||
return <Row>
|
|
||||||
<Col xs={1}>
|
|
||||||
<label>{t("Slot")}</label>
|
|
||||||
</Col>
|
|
||||||
<Col xs={2}>
|
|
||||||
<label>{t("X")}</label>
|
|
||||||
</Col>
|
|
||||||
<Col xs={2}>
|
|
||||||
<label>{t("Y")}</label>
|
|
||||||
</Col>
|
|
||||||
<Col xs={2}>
|
|
||||||
<label>{t("Z")}</label>
|
|
||||||
</Col>
|
|
||||||
<Col xs={4}>
|
|
||||||
<label>{t("Tool or Seed Container")}</label>
|
|
||||||
</Col>
|
|
||||||
</Row>;
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import { Row, Col, Widget, WidgetBody, WidgetHeader } from "../../ui";
|
|
||||||
import { ToolBayListProps } from "../interfaces";
|
|
||||||
import { TaggedToolSlotPointer } from "farmbot";
|
|
||||||
import { ToolBayHeader } from "./toolbay_header";
|
|
||||||
import { ToolTips } from "../../constants";
|
|
||||||
import { t } from "../../i18next_wrapper";
|
|
||||||
|
|
||||||
export class ToolBayList extends React.Component<ToolBayListProps, {}> {
|
|
||||||
|
|
||||||
ToolSlotListItem = (slot: TaggedToolSlotPointer, index: number) => {
|
|
||||||
const { getToolByToolSlotUUID } = this.props;
|
|
||||||
const tool = getToolByToolSlotUUID(slot.uuid);
|
|
||||||
const name = (tool?.body.name) || t("None");
|
|
||||||
return <Row key={slot.uuid}>
|
|
||||||
<Col xs={1}><label>{index + 1}</label></Col>
|
|
||||||
<Col xs={2}>{slot.body.gantry_mounted ? t("Gantry") : slot.body.x}</Col>
|
|
||||||
<Col xs={2}>{slot.body.y}</Col>
|
|
||||||
<Col xs={2}>{slot.body.z}</Col>
|
|
||||||
<Col xs={4}>{name}</Col>
|
|
||||||
</Row>;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return <Widget className="toolbay-list">
|
|
||||||
<WidgetHeader
|
|
||||||
helpText={ToolTips.TOOLBAY_LIST}
|
|
||||||
title={t("Tool Slots")}>
|
|
||||||
<button
|
|
||||||
className="gray fb-button"
|
|
||||||
onClick={this.props.toggle}>
|
|
||||||
{t("Edit")}
|
|
||||||
</button>
|
|
||||||
</WidgetHeader>
|
|
||||||
<WidgetBody>
|
|
||||||
<ToolBayHeader />
|
|
||||||
{this.props.getToolSlots().map(this.ToolSlotListItem)}
|
|
||||||
</WidgetBody>
|
|
||||||
</Widget>;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import { TaggedToolSlotPointer } from "farmbot";
|
|
||||||
import { Col, BlurableInput } from "../../ui";
|
|
||||||
import { edit } from "../../api/crud";
|
|
||||||
|
|
||||||
export interface TBNumColProps {
|
|
||||||
axis: "x" | "y" | "z";
|
|
||||||
value: number;
|
|
||||||
dispatch: Function;
|
|
||||||
slot: TaggedToolSlotPointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Used to display and edit the X/Y/Z numeric values in the tool bay form. */
|
|
||||||
export function ToolBayNumberCol(props: TBNumColProps) {
|
|
||||||
const { axis, value, dispatch, slot } = props;
|
|
||||||
return <Col xs={2}>
|
|
||||||
<BlurableInput
|
|
||||||
value={value.toString()}
|
|
||||||
onCommit={e =>
|
|
||||||
dispatch(edit(slot, { [axis]: parseFloat(e.currentTarget.value) }))}
|
|
||||||
type="number" />
|
|
||||||
</Col>;
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import { FBSelect, DropDownItem } from "../../ui";
|
|
||||||
import { TaggedToolSlotPointer } from "farmbot";
|
|
||||||
import { edit } from "../../api/crud";
|
|
||||||
import { isNumber } from "lodash";
|
|
||||||
import { ToolPulloutDirection } from "farmbot/dist/resources/api_resources";
|
|
||||||
import { t } from "../../i18next_wrapper";
|
|
||||||
|
|
||||||
export const DIRECTION_CHOICES_DDI: { [index: number]: DropDownItem } = {
|
|
||||||
[ToolPulloutDirection.NONE]:
|
|
||||||
{ label: t("None"), value: ToolPulloutDirection.NONE },
|
|
||||||
[ToolPulloutDirection.POSITIVE_X]:
|
|
||||||
{ label: t("Positive X"), value: ToolPulloutDirection.POSITIVE_X },
|
|
||||||
[ToolPulloutDirection.NEGATIVE_X]:
|
|
||||||
{ label: t("Negative X"), value: ToolPulloutDirection.NEGATIVE_X },
|
|
||||||
[ToolPulloutDirection.POSITIVE_Y]:
|
|
||||||
{ label: t("Positive Y"), value: ToolPulloutDirection.POSITIVE_Y },
|
|
||||||
[ToolPulloutDirection.NEGATIVE_Y]:
|
|
||||||
{ label: t("Negative Y"), value: ToolPulloutDirection.NEGATIVE_Y },
|
|
||||||
};
|
|
||||||
|
|
||||||
export const DIRECTION_CHOICES: DropDownItem[] = [
|
|
||||||
DIRECTION_CHOICES_DDI[ToolPulloutDirection.NONE],
|
|
||||||
DIRECTION_CHOICES_DDI[ToolPulloutDirection.POSITIVE_X],
|
|
||||||
DIRECTION_CHOICES_DDI[ToolPulloutDirection.NEGATIVE_X],
|
|
||||||
DIRECTION_CHOICES_DDI[ToolPulloutDirection.POSITIVE_Y],
|
|
||||||
DIRECTION_CHOICES_DDI[ToolPulloutDirection.NEGATIVE_Y],
|
|
||||||
];
|
|
||||||
|
|
||||||
export interface SlotDirectionSelectProps {
|
|
||||||
dispatch: Function;
|
|
||||||
slot: TaggedToolSlotPointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SlotDirectionSelect(props: SlotDirectionSelectProps) {
|
|
||||||
const { dispatch, slot } = props;
|
|
||||||
const direction = slot.body.pullout_direction;
|
|
||||||
|
|
||||||
const changePulloutDirection = (selectedDirection: DropDownItem) => {
|
|
||||||
const { value } = selectedDirection;
|
|
||||||
dispatch(edit(slot, {
|
|
||||||
pullout_direction: isNumber(value) ? value : parseInt(value)
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
return <FBSelect
|
|
||||||
list={DIRECTION_CHOICES}
|
|
||||||
selectedItem={DIRECTION_CHOICES_DDI[direction]}
|
|
||||||
onChange={changePulloutDirection} />;
|
|
||||||
}
|
|
|
@ -1,84 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import { isNumber } from "lodash";
|
|
||||||
import { BotPosition } from "../../devices/interfaces";
|
|
||||||
import { TaggedToolSlotPointer } from "farmbot";
|
|
||||||
import { edit } from "../../api/crud";
|
|
||||||
import { SlotDirectionSelect } from "./toolbay_slot_direction_selection";
|
|
||||||
import { ToolPulloutDirection } from "farmbot/dist/resources/api_resources";
|
|
||||||
import { t } from "../../i18next_wrapper";
|
|
||||||
|
|
||||||
export const positionIsDefined = (position: BotPosition): boolean =>
|
|
||||||
isNumber(position.x) && isNumber(position.y) && isNumber(position.z);
|
|
||||||
|
|
||||||
export const useCurrentPosition = (
|
|
||||||
dispatch: Function, slot: TaggedToolSlotPointer, position: BotPosition) => {
|
|
||||||
if (positionIsDefined(position)) {
|
|
||||||
dispatch(edit(slot, { x: position.x, y: position.y, z: position.z }));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const positionButtonTitle = (position: BotPosition): string =>
|
|
||||||
positionIsDefined(position)
|
|
||||||
? `(${position.x}, ${position.y}, ${position.z})`
|
|
||||||
: t("(unknown)");
|
|
||||||
|
|
||||||
export const newSlotDirection =
|
|
||||||
(old: ToolPulloutDirection | undefined): ToolPulloutDirection =>
|
|
||||||
isNumber(old) && old < 4 ? old + 1 : ToolPulloutDirection.NONE;
|
|
||||||
|
|
||||||
export const changePulloutDirection =
|
|
||||||
(dispatch: Function, slot: TaggedToolSlotPointer) => () => {
|
|
||||||
dispatch(edit(slot,
|
|
||||||
{ pullout_direction: newSlotDirection(slot.body.pullout_direction) }));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const directionIconClass = (slotDirection: ToolPulloutDirection) => {
|
|
||||||
switch (slotDirection) {
|
|
||||||
case ToolPulloutDirection.POSITIVE_X: return "fa fa-arrow-circle-right";
|
|
||||||
case ToolPulloutDirection.NEGATIVE_X: return "fa fa-arrow-circle-left";
|
|
||||||
case ToolPulloutDirection.POSITIVE_Y: return "fa fa-arrow-circle-up";
|
|
||||||
case ToolPulloutDirection.NEGATIVE_Y: return "fa fa-arrow-circle-down";
|
|
||||||
case ToolPulloutDirection.NONE: return "fa fa-dot-circle-o";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface SlotMenuProps {
|
|
||||||
dispatch: Function,
|
|
||||||
slot: TaggedToolSlotPointer,
|
|
||||||
botPosition: BotPosition
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SlotMenu = (props: SlotMenuProps) => {
|
|
||||||
const { dispatch, slot, botPosition } = props;
|
|
||||||
const { pullout_direction, gantry_mounted } = slot.body;
|
|
||||||
return <div className="toolbay-slot-menu">
|
|
||||||
<fieldset>
|
|
||||||
<label>
|
|
||||||
{t("Change slot direction")}
|
|
||||||
</label>
|
|
||||||
<i className={"direction-icon " + directionIconClass(pullout_direction)}
|
|
||||||
onClick={changePulloutDirection(dispatch, slot)} />
|
|
||||||
<SlotDirectionSelect
|
|
||||||
key={pullout_direction}
|
|
||||||
dispatch={dispatch}
|
|
||||||
slot={slot} />
|
|
||||||
</fieldset>
|
|
||||||
<fieldset>
|
|
||||||
<label>{t("Use current location")}</label>
|
|
||||||
<button
|
|
||||||
className="blue fb-button"
|
|
||||||
title={positionButtonTitle(botPosition)}
|
|
||||||
onClick={() => useCurrentPosition(dispatch, slot, botPosition)}>
|
|
||||||
<i className="fa fa-crosshairs" />
|
|
||||||
</button>
|
|
||||||
<p>{positionButtonTitle(botPosition)}</p>
|
|
||||||
</fieldset>
|
|
||||||
<fieldset>
|
|
||||||
<label>{t("Gantry-mounted")}</label>
|
|
||||||
<input type="checkbox"
|
|
||||||
onChange={() =>
|
|
||||||
dispatch(edit(slot, { gantry_mounted: !gantry_mounted }))}
|
|
||||||
checked={gantry_mounted} />
|
|
||||||
</fieldset>
|
|
||||||
</div>;
|
|
||||||
};
|
|
|
@ -1,51 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import { connect } from "react-redux";
|
|
||||||
import { ToolsState, Props } from "./interfaces";
|
|
||||||
import { Col, Row, Page } from "../ui";
|
|
||||||
import { ToolBayList, ToolBayForm, ToolList, ToolForm } from "./components";
|
|
||||||
import { mapStateToProps } from "./state_to_props";
|
|
||||||
|
|
||||||
export class RawTools extends React.Component<Props, Partial<ToolsState>> {
|
|
||||||
state: ToolsState = { editingBays: false, editingTools: false };
|
|
||||||
|
|
||||||
toggle = (name: keyof ToolsState) =>
|
|
||||||
() => this.setState({ [name]: !this.state[name] });
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return <Page className="tools-page">
|
|
||||||
<Row>
|
|
||||||
<Col sm={7}>
|
|
||||||
{this.state.editingBays
|
|
||||||
? <ToolBayForm
|
|
||||||
toggle={this.toggle("editingBays")}
|
|
||||||
dispatch={this.props.dispatch}
|
|
||||||
botPosition={this.props.botPosition}
|
|
||||||
toolSlots={this.props.toolSlots}
|
|
||||||
getToolSlots={this.props.getToolSlots}
|
|
||||||
getChosenToolOption={this.props.getChosenToolOption}
|
|
||||||
getToolOptions={this.props.getToolOptions}
|
|
||||||
changeToolSlot={this.props.changeToolSlot} />
|
|
||||||
: <ToolBayList
|
|
||||||
toggle={this.toggle("editingBays")}
|
|
||||||
getToolByToolSlotUUID={this.props.getToolByToolSlotUUID}
|
|
||||||
getToolSlots={this.props.getToolSlots} />}
|
|
||||||
</Col>
|
|
||||||
<Col sm={5}>
|
|
||||||
{this.state.editingTools
|
|
||||||
? <ToolForm
|
|
||||||
isActive={this.props.isActive}
|
|
||||||
toggle={this.toggle("editingTools")}
|
|
||||||
dispatch={this.props.dispatch}
|
|
||||||
tools={this.props.tools} />
|
|
||||||
: <ToolList
|
|
||||||
isActive={this.props.isActive}
|
|
||||||
toggle={this.toggle("editingTools")}
|
|
||||||
dispatch={this.props.dispatch}
|
|
||||||
tools={this.props.tools} />}
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</Page>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Tools = connect(mapStateToProps)(RawTools);
|
|
|
@ -1,56 +0,0 @@
|
||||||
import { DropDownItem } from "../ui/index";
|
|
||||||
import {
|
|
||||||
TaggedTool,
|
|
||||||
TaggedToolSlotPointer,
|
|
||||||
} from "farmbot";
|
|
||||||
import { BotPosition } from "../devices/interfaces";
|
|
||||||
|
|
||||||
export interface ToolsState {
|
|
||||||
editingTools: boolean;
|
|
||||||
editingBays: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Props {
|
|
||||||
toolSlots: TaggedToolSlotPointer[];
|
|
||||||
tools: TaggedTool[];
|
|
||||||
getToolOptions(): DropDownItem[];
|
|
||||||
getChosenToolOption(toolSlotUuid: string | undefined): DropDownItem;
|
|
||||||
getToolByToolSlotUUID(uuid: string): TaggedTool | undefined;
|
|
||||||
getToolSlots(): TaggedToolSlotPointer[];
|
|
||||||
dispatch: Function;
|
|
||||||
isActive: (tool: TaggedTool) => boolean;
|
|
||||||
changeToolSlot(t: TaggedToolSlotPointer, dispatch: Function):
|
|
||||||
(d: DropDownItem) => void;
|
|
||||||
botPosition: BotPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Tool {
|
|
||||||
id?: number | undefined;
|
|
||||||
name?: string;
|
|
||||||
status?: string | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ToolBayListProps {
|
|
||||||
toggle(): void;
|
|
||||||
getToolByToolSlotUUID(uuid: string): TaggedTool | undefined;
|
|
||||||
getToolSlots(): TaggedToolSlotPointer[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ToolBayFormProps {
|
|
||||||
dispatch: Function;
|
|
||||||
toolSlots: TaggedToolSlotPointer[];
|
|
||||||
botPosition: BotPosition;
|
|
||||||
toggle(): void;
|
|
||||||
getToolOptions(): DropDownItem[];
|
|
||||||
getChosenToolOption(uuid: string | undefined): DropDownItem;
|
|
||||||
getToolSlots(): TaggedToolSlotPointer[];
|
|
||||||
changeToolSlot(t: TaggedToolSlotPointer, dispatch: Function):
|
|
||||||
(d: DropDownItem) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ToolListAndFormProps {
|
|
||||||
dispatch: Function;
|
|
||||||
tools: TaggedTool[];
|
|
||||||
toggle(): void;
|
|
||||||
isActive(tool: TaggedTool): boolean;
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
import { Everything } from "../interfaces";
|
|
||||||
import { Props } from "./interfaces";
|
|
||||||
import {
|
|
||||||
selectAllToolSlotPointers,
|
|
||||||
selectAllTools,
|
|
||||||
currentToolInSlot,
|
|
||||||
} from "../resources/selectors";
|
|
||||||
import { isTaggedTool } from "../resources/tagged_resources";
|
|
||||||
import { edit } from "../api/crud";
|
|
||||||
import { DropDownItem, NULL_CHOICE } from "../ui";
|
|
||||||
import { validBotLocationData } from "../util";
|
|
||||||
import { TaggedTool, TaggedToolSlotPointer } from "farmbot";
|
|
||||||
import { isNumber, noop, compact } from "lodash";
|
|
||||||
|
|
||||||
export function mapStateToProps(props: Everything): Props {
|
|
||||||
const toolSlots = selectAllToolSlotPointers(props.resources.index);
|
|
||||||
const tools = selectAllTools(props.resources.index);
|
|
||||||
|
|
||||||
/** Returns sorted tool slots specific to the tool bay id passed. */
|
|
||||||
const getToolSlots = () => toolSlots;
|
|
||||||
|
|
||||||
/** Returns all tools in an <FBSelect /> compatible format. */
|
|
||||||
const getToolOptions = () => {
|
|
||||||
return compact(tools
|
|
||||||
.map(tool => ({
|
|
||||||
label: tool.body.name || "untitled",
|
|
||||||
value: tool.body.id || 0,
|
|
||||||
}))
|
|
||||||
.filter(ddi => isNumber(ddi.value) && ddi.value > 0));
|
|
||||||
};
|
|
||||||
|
|
||||||
const activeTools = compact(toolSlots.map(x => x.body.tool_id));
|
|
||||||
|
|
||||||
const isActive =
|
|
||||||
(t: TaggedTool) => !!(t.body.id && activeTools.includes(t.body.id));
|
|
||||||
|
|
||||||
const getToolByToolSlotUUID = currentToolInSlot(props.resources.index);
|
|
||||||
|
|
||||||
/** Returns the current tool chosen in a slot based off the slot's id
|
|
||||||
* and in an <FBSelect /> compatible format. */
|
|
||||||
const getChosenToolOption = (toolSlotUUID: string | undefined) => {
|
|
||||||
const chosenTool = toolSlotUUID && getToolByToolSlotUUID(toolSlotUUID);
|
|
||||||
return (chosenTool && isTaggedTool(chosenTool) && chosenTool.body.id)
|
|
||||||
? { label: chosenTool.body.name || "untitled", value: chosenTool.uuid }
|
|
||||||
: NULL_CHOICE;
|
|
||||||
};
|
|
||||||
|
|
||||||
const changeToolSlot = (t: TaggedToolSlotPointer,
|
|
||||||
dispatch: Function) =>
|
|
||||||
(d: DropDownItem) => {
|
|
||||||
// THIS IS IMPORTANT:
|
|
||||||
// If you remove the `any`, the tool will be serialized wrong and
|
|
||||||
// cause errors.
|
|
||||||
// tslint:disable-next-line:no-null-keyword no-any
|
|
||||||
const tool_id = d.value ? d.value : (null as any);
|
|
||||||
dispatch(edit(t, { tool_id }));
|
|
||||||
};
|
|
||||||
|
|
||||||
const botPosition =
|
|
||||||
validBotLocationData(props.bot.hardware.location_data).position;
|
|
||||||
|
|
||||||
return {
|
|
||||||
toolSlots,
|
|
||||||
tools,
|
|
||||||
getToolSlots,
|
|
||||||
getToolOptions,
|
|
||||||
getChosenToolOption,
|
|
||||||
getToolByToolSlotUUID,
|
|
||||||
changeToolSlot,
|
|
||||||
isActive,
|
|
||||||
dispatch: noop,
|
|
||||||
botPosition,
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue