remove old tools page
parent
9bd98aca1e
commit
90ddd78bb8
|
@ -30,7 +30,6 @@ import "../regimens/editor/interfaces";
|
|||
import "../regimens/interfaces";
|
||||
import "../resources/interfaces";
|
||||
import "../sequences/interfaces";
|
||||
import "../tools/interfaces";
|
||||
|
||||
describe("interfaces", () => {
|
||||
it("cant explain why coverage is 0 for interface files", () => {
|
||||
|
|
|
@ -1,18 +1,15 @@
|
|||
import React from "react";
|
||||
import { t } from "../../i18next_wrapper";
|
||||
import { Xyz, TaggedTool, TaggedToolSlotPointer } from "farmbot";
|
||||
import { Row, Col, BlurableInput, FBSelect, NULL_CHOICE, DropDownItem } from "../../ui";
|
||||
import {
|
||||
directionIconClass, positionButtonTitle, newSlotDirection, positionIsDefined
|
||||
} from "../../tools/components/toolbay_slot_menu";
|
||||
import {
|
||||
DIRECTION_CHOICES, DIRECTION_CHOICES_DDI
|
||||
} from "../../tools/components/toolbay_slot_direction_selection";
|
||||
Row, Col, BlurableInput, FBSelect, NULL_CHOICE, DropDownItem
|
||||
} from "../../ui";
|
||||
import { BotPosition } from "../../devices/interfaces";
|
||||
import { ToolPulloutDirection } from "farmbot/dist/resources/api_resources";
|
||||
import { Popover } from "@blueprintjs/core";
|
||||
import { ToolSlotSVG } from "../map/layers/tool_slots/tool_graphics";
|
||||
import { BotOriginQuadrant } from "../interfaces";
|
||||
import { isNumber } from "lodash";
|
||||
|
||||
export interface GantryMountedInputProps {
|
||||
gantryMounted: boolean;
|
||||
|
@ -189,3 +186,46 @@ export const SlotEditRows = (props: SlotEditRowsProps) =>
|
|||
gantryMounted={props.toolSlot.body.gantry_mounted}
|
||||
onChange={props.updateToolSlot} />}
|
||||
</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"),
|
||||
key: "Sequences",
|
||||
}),
|
||||
route({
|
||||
children: false,
|
||||
$: "/tools",
|
||||
getModule: () => import("./tools"),
|
||||
key: "Tools",
|
||||
}),
|
||||
route({
|
||||
children: false,
|
||||
$: "/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