- {label}
+ {point.name || t("Untitled point")}
{`(${point.x}, ${point.y}) ⌀${point.radius * 2}`}
diff --git a/frontend/farm_designer/reducer.ts b/frontend/farm_designer/reducer.ts
index 0c601afb1..81909dee2 100644
--- a/frontend/farm_designer/reducer.ts
+++ b/frontend/farm_designer/reducer.ts
@@ -1,14 +1,16 @@
-import { CropLiveSearchResult, CurrentPointPayl } from "./interfaces";
+import { CropLiveSearchResult, DrawnPointPayl, DrawnWeedPayl } from "./interfaces";
import { generateReducer } from "../redux/generate_reducer";
import { DesignerState, HoveredPlantPayl } from "./interfaces";
import { cloneDeep } from "lodash";
-import { TaggedResource } from "farmbot";
+import { TaggedResource, PointType } from "farmbot";
import { Actions } from "../constants";
import { BotPosition } from "../devices/interfaces";
import { PointGroupSortType } from "farmbot/dist/resources/api_resources";
+import { UUID } from "../resources/interfaces";
export const initialState: DesignerState = {
- selectedPlants: undefined,
+ selectedPoints: undefined,
+ selectionPointType: undefined,
hoveredPlant: {
plantUUID: undefined,
icon: ""
@@ -20,7 +22,8 @@ export const initialState: DesignerState = {
cropSearchResults: [],
cropSearchInProgress: false,
chosenLocation: { x: undefined, y: undefined, z: undefined },
- currentPoint: undefined,
+ drawnPoint: undefined,
+ drawnWeed: undefined,
openedSavedGarden: undefined,
tryGroupSortType: undefined,
editGroupAreaInMap: false,
@@ -41,10 +44,15 @@ export const designer = generateReducer(initialState)
s.cropSearchInProgress = false;
return s;
})
- .add(Actions.SELECT_PLANT, (s, { payload }) => {
- s.selectedPlants = payload;
+ .add(Actions.SELECT_POINT, (s, { payload }) => {
+ s.selectedPoints = payload;
return s;
})
+ .add(
+ Actions.SET_SELECTION_POINT_TYPE, (s, { payload }) => {
+ s.selectionPointType = payload;
+ return s;
+ })
.add(Actions.TOGGLE_HOVERED_PLANT, (s, { payload }) => {
s.hoveredPlant = payload;
return s;
@@ -61,12 +69,20 @@ export const designer = generateReducer(initialState)
s.hoveredToolSlot = payload;
return s;
})
- .add(
- Actions.SET_CURRENT_POINT_DATA, (s, { payload }) => {
+ .add(
+ Actions.SET_DRAWN_POINT_DATA, (s, { payload }) => {
const { color } = (!payload || !payload.color) ?
- (s.currentPoint || { color: "green" }) : payload;
- s.currentPoint = payload;
- s.currentPoint && (s.currentPoint.color = color);
+ (s.drawnPoint || { color: "green" }) : payload;
+ s.drawnPoint = payload;
+ s.drawnPoint && (s.drawnPoint.color = color);
+ return s;
+ })
+ .add(
+ Actions.SET_DRAWN_WEED_DATA, (s, { payload }) => {
+ const { color } = (!payload || !payload.color) ?
+ (s.drawnWeed || { color: "red" }) : payload;
+ s.drawnWeed = payload;
+ s.drawnWeed && (s.drawnWeed.color = color);
return s;
})
.add(Actions.OF_SEARCH_RESULTS_OK, (s, a) => {
@@ -75,7 +91,7 @@ export const designer = generateReducer(initialState)
return s;
})
.add(Actions.DESTROY_RESOURCE_OK, (s) => {
- s.selectedPlants = undefined;
+ s.selectedPoints = undefined;
s.hoveredPlant = { plantUUID: undefined, icon: "" };
return s;
})
diff --git a/frontend/farm_designer/saved_gardens/__tests__/saved_gardens_test.tsx b/frontend/farm_designer/saved_gardens/__tests__/saved_gardens_test.tsx
index 8ffe8ae67..18387a72c 100644
--- a/frontend/farm_designer/saved_gardens/__tests__/saved_gardens_test.tsx
+++ b/frontend/farm_designer/saved_gardens/__tests__/saved_gardens_test.tsx
@@ -125,7 +125,7 @@ describe("", () => {
clickButton(wrapper, 1, "edit");
expect(history.push).toHaveBeenCalledWith("/app/designer/plants");
expect(dispatch).toHaveBeenCalledWith({
- type: Actions.SELECT_PLANT,
+ type: Actions.SELECT_POINT,
payload: undefined
});
});
diff --git a/frontend/farm_designer/state_to_props.ts b/frontend/farm_designer/state_to_props.ts
index 09c29706b..3ca29cfb9 100644
--- a/frontend/farm_designer/state_to_props.ts
+++ b/frontend/farm_designer/state_to_props.ts
@@ -14,6 +14,7 @@ import {
selectAllPointGroups,
getDeviceAccountSettings,
maybeFindToolById,
+ selectAllWeedPointers,
} from "../resources/selectors";
import { validBotLocationData, validFwConfig, unpackUUID } from "../util";
import { getWebAppConfigValue } from "../config_storage/actions";
@@ -44,10 +45,10 @@ export function mapStateToProps(props: Everything): Props {
const plants = getPlants(props.resources);
const findPlant = plantFinder(plants);
- const { selectedPlants } = props.resources.consumers.farm_designer;
- const selectedPlant = selectedPlants ? findPlant(selectedPlants[0]) : undefined;
- const { plantUUID } = props.resources.consumers.farm_designer.hoveredPlant;
+ const { selectedPoints } = props.resources.consumers.farm_designer;
+ const selectedPlant = selectedPoints ? findPlant(selectedPoints[0]) : undefined;
+ const { plantUUID } = props.resources.consumers.farm_designer.hoveredPlant;
const hoveredPlant = findPlant(plantUUID);
const getConfigValue = getWebAppConfigValue(() => props);
@@ -55,6 +56,7 @@ export function mapStateToProps(props: Everything): Props {
const genericPoints = getConfigValue(BooleanSetting.show_historic_points)
? allGenericPoints
: allGenericPoints.filter(x => !x.body.discarded_at);
+ const weeds = selectAllWeedPointers(props.resources.index);
const fwConfig = validFwConfig(getFirmwareConfig(props.resources.index));
const { mcu_params } = props.bot.hardware;
@@ -113,6 +115,7 @@ export function mapStateToProps(props: Everything): Props {
selectedPlant,
designer: props.resources.consumers.farm_designer,
genericPoints,
+ weeds,
allPoints: selectAllPoints(props.resources.index),
toolSlots: joinToolsAndSlot(props.resources.index),
hoveredPlant,
diff --git a/frontend/farm_designer/tools/__tests__/index_test.tsx b/frontend/farm_designer/tools/__tests__/index_test.tsx
index 13362abed..66a735318 100644
--- a/frontend/farm_designer/tools/__tests__/index_test.tsx
+++ b/frontend/farm_designer/tools/__tests__/index_test.tsx
@@ -1,6 +1,7 @@
+let mockPath = "/app/designer/tools";
jest.mock("../../../history", () => ({
history: { push: jest.fn() },
- getPathArray: () => "/app/designer/tools".split("/"),
+ getPathArray: () => mockPath.split("/"),
}));
jest.mock("../../../api/crud", () => ({
@@ -8,6 +9,10 @@ jest.mock("../../../api/crud", () => ({
save: jest.fn(),
}));
+jest.mock("../../map/actions", () => ({
+ mapPointClickAction: jest.fn(() => jest.fn()),
+}));
+
const mockDevice = { readPin: jest.fn(() => Promise.resolve()) };
jest.mock("../../../device", () => ({ getDevice: () => mockDevice }));
@@ -28,6 +33,7 @@ import { Content, Actions } from "../../../constants";
import { edit, save } from "../../../api/crud";
import { ToolSelection } from "../tool_slot_edit_components";
import { ToolsProps } from "../interfaces";
+import { mapPointClickAction } from "../../map/actions";
describe("", () => {
const fakeProps = (): ToolsProps => ({
@@ -245,4 +251,37 @@ describe("", () => {
wrapper.find(".tool-selection-wrapper").first().simulate("click", e);
expect(e.stopPropagation).toHaveBeenCalled();
});
+
+ it("shows tool name", () => {
+ const p = fakeProps();
+ p.hideDropdown = true;
+ const wrapper = mount();
+ expect(wrapper.text().toLowerCase()).toContain("empty");
+ });
+
+ it("opens tool slot", () => {
+ mockPath = "/app/designer/tool-slots";
+ const p = fakeProps();
+ p.toolSlot.body.id = 1;
+ const wrapper = shallow();
+ wrapper.find("div").first().simulate("click");
+ expect(mapPointClickAction).not.toHaveBeenCalled();
+ expect(history.push).toHaveBeenCalledWith("/app/designer/tool-slots/1");
+ expect(p.dispatch).not.toHaveBeenCalled();
+ });
+
+ it("removes item in box select mode", () => {
+ mockPath = "/app/designer/plants/select";
+ const p = fakeProps();
+ p.toolSlot.body.id = 1;
+ const wrapper = shallow();
+ wrapper.find("div").first().simulate("click");
+ expect(mapPointClickAction).toHaveBeenCalledWith(expect.any(Function),
+ p.toolSlot.uuid);
+ expect(history.push).not.toHaveBeenCalled();
+ expect(p.dispatch).toHaveBeenCalledWith({
+ type: Actions.HOVER_TOOL_SLOT,
+ payload: undefined,
+ });
+ });
});
diff --git a/frontend/farm_designer/tools/index.tsx b/frontend/farm_designer/tools/index.tsx
index 8b48a8ac9..36648d70c 100644
--- a/frontend/farm_designer/tools/index.tsx
+++ b/frontend/farm_designer/tools/index.tsx
@@ -26,6 +26,9 @@ import { hasUTM } from "../../devices/components/firmware_hardware_support";
import { ToolsProps, ToolsState } from "./interfaces";
import { mapStateToProps } from "./state_to_props";
import { BotOriginQuadrant } from "../interfaces";
+import { mapPointClickAction } from "../map/actions";
+import { getMode } from "../map/util";
+import { Mode } from "../map/interfaces";
const toolStatus = (value: number | undefined): string => {
switch (value) {
@@ -207,6 +210,7 @@ export interface ToolSlotInventoryItemProps {
isActive(id: number | undefined): boolean;
xySwap: boolean;
quadrant: BotOriginQuadrant;
+ hideDropdown?: boolean;
}
export const ToolSlotInventoryItem = (props: ToolSlotInventoryItemProps) => {
@@ -215,7 +219,14 @@ export const ToolSlotInventoryItem = (props: ToolSlotInventoryItemProps) => {
.filter(tool => tool.body.id == tool_id)[0]?.body.name;
return history.push(`/app/designer/tool-slots/${id}`)}
+ onClick={() => {
+ if (getMode() == Mode.boxSelect) {
+ mapPointClickAction(props.dispatch, props.toolSlot.uuid)();
+ props.dispatch(setToolHover(undefined));
+ } else {
+ history.push(`/app/designer/tool-slots/${id}`);
+ }
+ }}
onMouseEnter={() => props.dispatch(setToolHover(props.toolSlot.uuid))}
onMouseLeave={() => props.dispatch(setToolHover(undefined))}>
@@ -226,20 +237,24 @@ export const ToolSlotInventoryItem = (props: ToolSlotInventoryItemProps) => {
xySwap={props.xySwap} quadrant={props.quadrant} />
- e.stopPropagation()}>
- tool.body.id == tool_id)[0]}
- onChange={update => {
- props.dispatch(edit(props.toolSlot, update));
- props.dispatch(save(props.toolSlot.uuid));
- }}
- isActive={props.isActive}
- filterSelectedTool={false}
- filterActiveTools={true} />
-
+ {props.hideDropdown
+ ?
+ {toolName || t("Empty")}
+
+ : e.stopPropagation()}>
+ tool.body.id == tool_id)[0]}
+ onChange={update => {
+ props.dispatch(edit(props.toolSlot, update));
+ props.dispatch(save(props.toolSlot.uuid));
+ }}
+ isActive={props.isActive}
+ filterSelectedTool={false}
+ filterActiveTools={true} />
+
}
diff --git a/frontend/farm_designer/weeds/__tests__/weed_inventory_item_test.tsx b/frontend/farm_designer/weeds/__tests__/weed_inventory_item_test.tsx
new file mode 100644
index 000000000..125b0e697
--- /dev/null
+++ b/frontend/farm_designer/weeds/__tests__/weed_inventory_item_test.tsx
@@ -0,0 +1,81 @@
+let mockPath = "/app/designer/weeds";
+jest.mock("../../../history", () => ({
+ push: jest.fn(),
+ getPathArray: () => mockPath.split("/"),
+}));
+
+jest.mock("../../map/actions", () => ({
+ mapPointClickAction: jest.fn(() => jest.fn()),
+}));
+
+import * as React from "react";
+import { shallow } from "enzyme";
+import {
+ WeedInventoryItem, WeedInventoryItemProps,
+} from "../weed_inventory_item";
+import { fakeWeed } from "../../../__test_support__/fake_state/resources";
+import { push } from "../../../history";
+import { Actions } from "../../../constants";
+import { mapPointClickAction } from "../../map/actions";
+
+describe(" />", () => {
+ const fakeProps = (): WeedInventoryItemProps => ({
+ tpp: fakeWeed(),
+ dispatch: jest.fn(),
+ hovered: false,
+ });
+
+ it("navigates to weed", () => {
+ const p = fakeProps();
+ p.tpp.body.id = 1;
+ const wrapper = shallow();
+ wrapper.simulate("click");
+ expect(mapPointClickAction).not.toHaveBeenCalled();
+ expect(push).toHaveBeenCalledWith("/app/designer/weeds/1");
+ expect(p.dispatch).toHaveBeenCalledWith({
+ type: Actions.TOGGLE_HOVERED_POINT,
+ payload: [p.tpp.uuid],
+ });
+ });
+
+ it("removes item in box select mode", () => {
+ mockPath = "/app/designer/plants/select";
+ const p = fakeProps();
+ const wrapper = shallow();
+ wrapper.simulate("click");
+ expect(mapPointClickAction).toHaveBeenCalledWith(expect.any(Function),
+ p.tpp.uuid);
+ expect(push).not.toHaveBeenCalled();
+ expect(p.dispatch).toHaveBeenCalledWith({
+ type: Actions.TOGGLE_HOVERED_POINT,
+ payload: undefined,
+ });
+ });
+
+ it("hovers weed", () => {
+ const p = fakeProps();
+ p.tpp.body.id = 1;
+ const wrapper = shallow();
+ wrapper.simulate("mouseEnter");
+ expect(p.dispatch).toHaveBeenCalledWith({
+ type: Actions.TOGGLE_HOVERED_POINT, payload: p.tpp.uuid
+ });
+ });
+
+ it("shows hovered", () => {
+ const p = fakeProps();
+ p.hovered = true;
+ const wrapper = shallow();
+ expect(wrapper.hasClass("hovered")).toBeTruthy();
+ });
+
+ it("un-hovers weed", () => {
+ const p = fakeProps();
+ p.tpp.body.id = 1;
+ const wrapper = shallow();
+ wrapper.simulate("mouseLeave");
+ expect(p.dispatch).toHaveBeenCalledWith({
+ type: Actions.TOGGLE_HOVERED_POINT, payload: undefined
+ });
+ });
+});
diff --git a/frontend/farm_designer/points/__tests__/weeds_edit_test.tsx b/frontend/farm_designer/weeds/__tests__/weeds_edit_test.tsx
similarity index 80%
rename from frontend/farm_designer/points/__tests__/weeds_edit_test.tsx
rename to frontend/farm_designer/weeds/__tests__/weeds_edit_test.tsx
index 9569ac587..fdfdc7516 100644
--- a/frontend/farm_designer/points/__tests__/weeds_edit_test.tsx
+++ b/frontend/farm_designer/weeds/__tests__/weeds_edit_test.tsx
@@ -9,7 +9,7 @@ import { mount, shallow } from "enzyme";
import {
RawEditWeed as EditWeed, EditWeedProps, mapStateToProps,
} from "../weeds_edit";
-import { fakePoint } from "../../../__test_support__/fake_state/resources";
+import { fakeWeed } from "../../../__test_support__/fake_state/resources";
import { fakeState } from "../../../__test_support__/fake_state";
import {
buildResourceIndex,
@@ -32,9 +32,9 @@ describe("", () => {
it("renders", () => {
mockPath = "/app/designer/weeds/1";
const p = fakeProps();
- const point = fakePoint();
- point.body.id = 1;
- p.findPoint = () => point;
+ const weed = fakeWeed();
+ weed.body.id = 1;
+ p.findPoint = () => weed;
const wrapper = mount();
expect(wrapper.text().toLowerCase()).toContain("edit");
});
@@ -42,9 +42,9 @@ describe("", () => {
it("goes back", () => {
mockPath = "/app/designer/weeds/1";
const p = fakeProps();
- const point = fakePoint();
- point.body.id = 1;
- p.findPoint = () => point;
+ const weed = fakeWeed();
+ weed.body.id = 1;
+ p.findPoint = () => weed;
const wrapper = shallow();
wrapper.find(DesignerPanelHeader).simulate("back");
expect(p.dispatch).toHaveBeenCalledWith({
@@ -56,10 +56,10 @@ describe("", () => {
describe("mapStateToProps()", () => {
it("returns props", () => {
const state = fakeState();
- const point = fakePoint();
- point.body.id = 1;
- state.resources = buildResourceIndex([point]);
+ const weed = fakeWeed();
+ weed.body.id = 1;
+ state.resources = buildResourceIndex([weed]);
const props = mapStateToProps(state);
- expect(props.findPoint(1)).toEqual(point);
+ expect(props.findPoint(1)).toEqual(weed);
});
});
diff --git a/frontend/farm_designer/points/__tests__/weeds_inventory_test.tsx b/frontend/farm_designer/weeds/__tests__/weeds_inventory_test.tsx
similarity index 83%
rename from frontend/farm_designer/points/__tests__/weeds_inventory_test.tsx
rename to frontend/farm_designer/weeds/__tests__/weeds_inventory_test.tsx
index 638f79066..b76423aef 100644
--- a/frontend/farm_designer/points/__tests__/weeds_inventory_test.tsx
+++ b/frontend/farm_designer/weeds/__tests__/weeds_inventory_test.tsx
@@ -4,11 +4,11 @@ import {
RawWeeds as Weeds, WeedsProps, mapStateToProps,
} from "../weeds_inventory";
import { fakeState } from "../../../__test_support__/fake_state";
-import { fakePoint } from "../../../__test_support__/fake_state/resources";
+import { fakeWeed } from "../../../__test_support__/fake_state/resources";
describe(" />", () => {
const fakeProps = (): WeedsProps => ({
- genericPoints: [],
+ weeds: [],
dispatch: jest.fn(),
hoveredPoint: undefined,
});
@@ -27,9 +27,9 @@ describe(" />", () => {
it("filters points", () => {
const p = fakeProps();
- p.genericPoints = [fakePoint(), fakePoint()];
- p.genericPoints[0].body.name = "weed 0";
- p.genericPoints[1].body.name = "weed 1";
+ p.weeds = [fakeWeed(), fakeWeed()];
+ p.weeds[0].body.name = "weed 0";
+ p.weeds[1].body.name = "weed 1";
const wrapper = mount();
wrapper.setState({ searchTerm: "0" });
expect(wrapper.text()).toContain("weed 0");
diff --git a/frontend/farm_designer/weeds/weed_inventory_item.tsx b/frontend/farm_designer/weeds/weed_inventory_item.tsx
new file mode 100644
index 000000000..0eb19b476
--- /dev/null
+++ b/frontend/farm_designer/weeds/weed_inventory_item.tsx
@@ -0,0 +1,69 @@
+import * as React from "react";
+import { TaggedWeedPointer } from "farmbot";
+import { Actions } from "../../constants";
+import { push } from "../../history";
+import { t } from "../../i18next_wrapper";
+import { DEFAULT_WEED_ICON } from "../map/layers/weeds/garden_weed";
+import { svgToUrl } from "../../open_farm/icons";
+import { genericWeedIcon } from "../point_groups/point_group_item";
+import { getMode } from "../map/util";
+import { Mode } from "../map/interfaces";
+import { mapPointClickAction } from "../map/actions";
+
+export interface WeedInventoryItemProps {
+ tpp: TaggedWeedPointer;
+ dispatch: Function;
+ hovered: boolean;
+}
+
+export class WeedInventoryItem extends
+ React.Component {
+
+ render() {
+ const weed = this.props.tpp.body;
+ const { tpp, dispatch } = this.props;
+ const weedId = (weed.id || "ERR_NO_POINT_ID").toString();
+
+ const toggle = (action: "enter" | "leave") => {
+ const isEnter = action === "enter";
+ dispatch({
+ type: Actions.TOGGLE_HOVERED_POINT,
+ payload: isEnter ? tpp.uuid : undefined
+ });
+ };
+
+ const click = () => {
+ if (getMode() == Mode.boxSelect) {
+ mapPointClickAction(dispatch, tpp.uuid)();
+ toggle("leave");
+ } else {
+ push(`/app/designer/weeds/${weedId}`);
+ dispatch({ type: Actions.TOGGLE_HOVERED_POINT, payload: [tpp.uuid] });
+ }
+ };
+
+ return toggle("enter")}
+ onMouseLeave={() => toggle("leave")}
+ onClick={click}>
+
+
+
+
+
+ {weed.name || t("Untitled weed")}
+
+
+ {`(${weed.x}, ${weed.y}) ⌀${weed.radius * 2}`}
+
+
;
+ }
+}
diff --git a/frontend/farm_designer/points/weeds_edit.tsx b/frontend/farm_designer/weeds/weeds_edit.tsx
similarity index 87%
rename from frontend/farm_designer/points/weeds_edit.tsx
rename to frontend/farm_designer/weeds/weeds_edit.tsx
index 04c4ac70f..e15cbf5b2 100644
--- a/frontend/farm_designer/points/weeds_edit.tsx
+++ b/frontend/farm_designer/weeds/weeds_edit.tsx
@@ -6,22 +6,22 @@ import {
import { t } from "../../i18next_wrapper";
import { history, getPathArray } from "../../history";
import { Everything } from "../../interfaces";
-import { TaggedGenericPointer } from "farmbot";
-import { maybeFindGenericPointerById } from "../../resources/selectors";
+import { TaggedWeedPointer } from "farmbot";
+import { maybeFindWeedPointerById } from "../../resources/selectors";
import { Panel } from "../panel_header";
import {
EditPointProperties, PointActions, updatePoint,
-} from "./point_edit_actions";
+} from "../points/point_edit_actions";
import { Actions } from "../../constants";
export interface EditWeedProps {
dispatch: Function;
- findPoint(id: number): TaggedGenericPointer | undefined;
+ findPoint(id: number): TaggedWeedPointer | undefined;
}
export const mapStateToProps = (props: Everything): EditWeedProps => ({
dispatch: props.dispatch,
- findPoint: id => maybeFindGenericPointerById(props.resources.index, id),
+ findPoint: id => maybeFindWeedPointerById(props.resources.index, id),
});
export class RawEditWeed extends React.Component {
diff --git a/frontend/farm_designer/points/weeds_inventory.tsx b/frontend/farm_designer/weeds/weeds_inventory.tsx
similarity index 75%
rename from frontend/farm_designer/points/weeds_inventory.tsx
rename to frontend/farm_designer/weeds/weeds_inventory.tsx
index f4517bac2..50b3d1c0d 100644
--- a/frontend/farm_designer/points/weeds_inventory.tsx
+++ b/frontend/farm_designer/weeds/weeds_inventory.tsx
@@ -10,12 +10,12 @@ import {
DesignerPanel, DesignerPanelContent, DesignerPanelTop,
} from "../designer_panel";
import { t } from "../../i18next_wrapper";
-import { TaggedGenericPointer } from "farmbot";
-import { selectAllGenericPointers } from "../../resources/selectors";
-import { PointInventoryItem } from "./point_inventory_item";
+import { TaggedWeedPointer } from "farmbot";
+import { selectAllWeedPointers } from "../../resources/selectors";
+import { WeedInventoryItem } from "./weed_inventory_item";
export interface WeedsProps {
- genericPoints: TaggedGenericPointer[];
+ weeds: TaggedWeedPointer[];
dispatch: Function;
hoveredPoint: string | undefined;
}
@@ -24,13 +24,8 @@ interface WeedsState {
searchTerm: string;
}
-export const isAWeed = (pointName: string, type?: string) =>
- type == "weed" || pointName.toLowerCase().includes("weed");
-
export const mapStateToProps = (props: Everything): WeedsProps => ({
- genericPoints: selectAllGenericPointers(props.resources.index)
- .filter(x => !x.body.discarded_at)
- .filter(x => isAWeed(x.body.name, x.body.meta.type)),
+ weeds: selectAllWeedPointers(props.resources.index),
dispatch: props.dispatch,
hoveredPoint: props.resources.consumers.farm_designer.hoveredPoint,
});
@@ -54,17 +49,16 @@ export class RawWeeds extends React.Component {
0}
+ notEmpty={this.props.weeds.length > 0}
graphic={EmptyStateGraphic.weeds}
title={t("No weeds yet.")}
text={Content.NO_WEEDS}
colorScheme={"weeds"}>
- {this.props.genericPoints
+ {this.props.weeds
.filter(p => p.body.name.toLowerCase()
.includes(this.state.searchTerm.toLowerCase()))
- .map(p => )}
diff --git a/frontend/farmware/weed_detector/__tests__/actions_tests.ts b/frontend/farmware/weed_detector/__tests__/actions_tests.ts
index e321cd544..ab8386018 100644
--- a/frontend/farmware/weed_detector/__tests__/actions_tests.ts
+++ b/frontend/farmware/weed_detector/__tests__/actions_tests.ts
@@ -62,7 +62,8 @@ describe("deletePoints()", () => {
mockDelete = Promise.resolve();
mockData = [{ id: 1 }, { id: 2 }, { id: 3 }];
const dispatch = jest.fn();
- await deletePoints("weeds", { created_by: "plant-detection" })(dispatch, jest.fn());
+ const query = { meta: { created_by: "plant-detection" } };
+ await deletePoints("weeds", query)(dispatch, jest.fn());
expect(axios.post).toHaveBeenCalledWith("http://localhost/api/points/search",
{ meta: { created_by: "plant-detection" } });
await expect(axios.delete).toHaveBeenCalledWith("http://localhost/api/points/1,2,3");
@@ -80,7 +81,8 @@ describe("deletePoints()", () => {
mockDelete = Promise.reject("error");
mockData = [{ id: 1 }, { id: 2 }, { id: 3 }];
const dispatch = jest.fn();
- await deletePoints("weeds", { created_by: "plant-detection" })(dispatch, jest.fn());
+ const query = { meta: { created_by: "plant-detection" } };
+ await deletePoints("weeds", query)(dispatch, jest.fn());
expect(axios.post).toHaveBeenCalledWith("http://localhost/api/points/search",
{ meta: { created_by: "plant-detection" } });
await expect(axios.delete).toHaveBeenCalledWith("http://localhost/api/points/1,2,3");
@@ -98,7 +100,8 @@ describe("deletePoints()", () => {
mockDelete = Promise.resolve();
mockData = times(200, () => ({ id: 1 }));
const dispatch = jest.fn();
- await deletePoints("weeds", { created_by: "plant-detection" })(dispatch, jest.fn());
+ const query = { meta: { created_by: "plant-detection" } };
+ await deletePoints("weeds", query)(dispatch, jest.fn());
expect(axios.post).toHaveBeenCalledWith("http://localhost/api/points/search",
{ meta: { created_by: "plant-detection" } });
await expect(axios.delete).toHaveBeenCalledWith(
diff --git a/frontend/farmware/weed_detector/__tests__/weed_detector_test.tsx b/frontend/farmware/weed_detector/__tests__/weed_detector_test.tsx
index 2dd572f5a..799d561c3 100644
--- a/frontend/farmware/weed_detector/__tests__/weed_detector_test.tsx
+++ b/frontend/farmware/weed_detector/__tests__/weed_detector_test.tsx
@@ -85,7 +85,7 @@ describe("", () => {
expect(wrapper.instance().state.deletionProgress).toBeUndefined();
clickButton(wrapper, 1, "clear weeds");
expect(deletePoints).toHaveBeenCalledWith(
- "weeds", { created_by: "plant-detection" }, expect.any(Function));
+ "weeds", { meta: { created_by: "plant-detection" } }, expect.any(Function));
expect(wrapper.instance().state.deletionProgress).toEqual("Deleting...");
const fakeProgress = { completed: 50, total: 100, isDone: false };
mockDeletePoints.mock.calls[0][2](fakeProgress);
diff --git a/frontend/farmware/weed_detector/actions.tsx b/frontend/farmware/weed_detector/actions.tsx
index 0c1b20626..10511bedd 100644
--- a/frontend/farmware/weed_detector/actions.tsx
+++ b/frontend/farmware/weed_detector/actions.tsx
@@ -5,20 +5,19 @@ import { API } from "../../api";
import { Progress, ProgressCallback, trim } from "../../util";
import { getDevice } from "../../device";
import { noop, chunk } from "lodash";
-import { GenericPointer } from "farmbot/dist/resources/api_resources";
+import { Point } from "farmbot/dist/resources/api_resources";
import { Actions } from "../../constants";
import { t } from "../../i18next_wrapper";
export function deletePoints(
pointName: string,
- metaQuery: { [key: string]: string },
+ query: Partial,
cb?: ProgressCallback): Thunk {
// TODO: Generalize and add to api/crud.ts
return async function (dispatch) {
const URL = API.current.pointSearchPath;
- const QUERY = { meta: metaQuery };
try {
- const resp = await axios.post(URL, QUERY);
+ const resp = await axios.post(URL, query);
const ids = resp.data.map(x => x.id);
// If you delete too many points, you will violate the URL length
// limitation of 2,083. Chunking helps fix that.
diff --git a/frontend/farmware/weed_detector/index.tsx b/frontend/farmware/weed_detector/index.tsx
index 766366ace..ced91eec0 100644
--- a/frontend/farmware/weed_detector/index.tsx
+++ b/frontend/farmware/weed_detector/index.tsx
@@ -39,7 +39,7 @@ export class WeedDetector
this.setState({ deletionProgress: p.isDone ? "" : percentage });
};
this.props.dispatch(deletePoints(t("weeds"),
- { created_by: "plant-detection" }, progress));
+ { meta: { created_by: "plant-detection" } }, progress));
this.setState({ deletionProgress: t("Deleting...") });
}
diff --git a/frontend/resources/__tests__/selectors_test.ts b/frontend/resources/__tests__/selectors_test.ts
index f3041a578..9018d5529 100644
--- a/frontend/resources/__tests__/selectors_test.ts
+++ b/frontend/resources/__tests__/selectors_test.ts
@@ -111,7 +111,7 @@ describe("isKind()", () => {
describe("groupPointsByType()", () => {
it("returns points", () => {
const points = Selector.groupPointsByType(fakeIndex);
- const expectedKeys = ["Plant", "GenericPointer", "ToolSlot"];
+ const expectedKeys = ["Plant", "GenericPointer", "ToolSlot", "Weed"];
expect(expectedKeys.every(key => key in points)).toBeTruthy();
});
});
diff --git a/frontend/resources/selectors.ts b/frontend/resources/selectors.ts
index fbc60d8c3..0f3569422 100644
--- a/frontend/resources/selectors.ts
+++ b/frontend/resources/selectors.ts
@@ -10,15 +10,16 @@ import {
TaggedToolSlotPointer,
TaggedUser,
TaggedDevice,
+ TaggedWeedPointer,
} from "farmbot";
import {
isTaggedPlantPointer,
isTaggedGenericPointer,
isTaggedRegimen,
isTaggedSequence,
- isTaggedTool,
isTaggedToolSlotPointer,
sanityCheck,
+ isTaggedWeedPointer,
} from "./tagged_resources";
import { betterCompact, bail } from "../util";
import { findAllById } from "./selectors_by_id";
@@ -94,6 +95,13 @@ export function selectAllGenericPointers(index: ResourceIndex):
return betterCompact(genericPointers);
}
+export function selectAllWeedPointers(index: ResourceIndex):
+ TaggedWeedPointer[] {
+ const weedPointers = selectAllPoints(index)
+ .map(p => isTaggedWeedPointer(p) ? p : undefined);
+ return betterCompact(weedPointers);
+}
+
export function selectAllPlantPointers(index: ResourceIndex): TaggedPlantPointer[] {
const plantPointers = selectAllActivePoints(index)
.map(p => isTaggedPlantPointer(p) ? p : undefined);
@@ -141,22 +149,6 @@ export function getSequenceByUUID(index: ResourceIndex,
}
}
-/** GIVEN: a slot UUID.
- * FINDS: Tool in that slot (if any) */
-export const currentToolInSlot = (index: ResourceIndex) =>
- (toolSlotUUID: string): TaggedTool | undefined => {
- const currentSlot = selectCurrentToolSlot(index, toolSlotUUID);
- if (currentSlot
- && currentSlot.kind === "Point") {
- const toolUUID = index
- .byKindAndId[joinKindAndId("Tool", currentSlot.body.tool_id)];
- const tool = index.references[toolUUID || "NOPE!"];
- if (tool && isTaggedTool(tool)) {
- return tool;
- }
- }
- };
-
/** FINDS: All tools that are in use. */
export function toolsInUse(index: ResourceIndex): TaggedTool[] {
const ids = betterCompact(selectAllToolSlotPointers(index)
diff --git a/frontend/resources/selectors_by_id.ts b/frontend/resources/selectors_by_id.ts
index 2f42a1d49..361c71059 100644
--- a/frontend/resources/selectors_by_id.ts
+++ b/frontend/resources/selectors_by_id.ts
@@ -11,6 +11,7 @@ import {
isTaggedGenericPointer,
isTaggedSavedGarden,
isTaggedFolder,
+ isTaggedWeedPointer,
} from "./tagged_resources";
import {
ResourceName,
@@ -125,6 +126,13 @@ export function maybeFindGenericPointerById(index: ResourceIndex, id: number) {
if (resource && isTaggedGenericPointer(resource)) { return resource; }
}
+/** Unlike other findById methods, this one allows undefined (missed) values */
+export function maybeFindWeedPointerById(index: ResourceIndex, id: number) {
+ const uuid = index.byKindAndId[joinKindAndId("Point", id)];
+ const resource = index.references[uuid || "nope"];
+ if (resource && isTaggedWeedPointer(resource)) { return resource; }
+}
+
/** Unlike other findById methods, this one allows undefined (missed) values */
export function maybeFindSavedGardenById(index: ResourceIndex, id: number) {
const uuid = index.byKindAndId[joinKindAndId("SavedGarden", id)];
diff --git a/frontend/resources/tagged_resources.ts b/frontend/resources/tagged_resources.ts
index ab83ec9ab..67fc8c430 100644
--- a/frontend/resources/tagged_resources.ts
+++ b/frontend/resources/tagged_resources.ts
@@ -16,6 +16,7 @@ import {
TaggedPlantTemplate,
TaggedSavedGarden,
TaggedPointGroup,
+ TaggedWeedPointer,
} from "farmbot";
export interface TaggedResourceBase {
@@ -102,6 +103,8 @@ export const isTaggedGenericPointer =
(x: object): x is TaggedGenericPointer => {
return isTaggedPoint(x) && (x.body.pointer_type === "GenericPointer");
};
+export const isTaggedWeedPointer = (x: object): x is TaggedWeedPointer =>
+ isTaggedPoint(x) && (x.body.pointer_type === "Weed");
export const isTaggedSavedGarden =
(x: object): x is TaggedSavedGarden => is("SavedGarden")(x);
export const isTaggedPlantTemplate =
diff --git a/frontend/route_config.tsx b/frontend/route_config.tsx
index 77236b088..cd5539edf 100644
--- a/frontend/route_config.tsx
+++ b/frontend/route_config.tsx
@@ -376,7 +376,7 @@ export const UNBOUND_ROUTES = [
$: "/designer/weeds",
getModule,
key,
- getChild: () => import("./farm_designer/points/weeds_inventory"),
+ getChild: () => import("./farm_designer/weeds/weeds_inventory"),
childKey: "Weeds"
}),
route({
@@ -392,7 +392,7 @@ export const UNBOUND_ROUTES = [
$: "/designer/weeds/:point_id",
getModule,
key,
- getChild: () => import("./farm_designer/points/weeds_edit"),
+ getChild: () => import("./farm_designer/weeds/weeds_edit"),
childKey: "EditWeed"
}),
route({
diff --git a/frontend/sequences/locals_list/__tests__/location_form_list_test.ts b/frontend/sequences/locals_list/__tests__/location_form_list_test.ts
index 4606e3efa..4cd988f93 100644
--- a/frontend/sequences/locals_list/__tests__/location_form_list_test.ts
+++ b/frontend/sequences/locals_list/__tests__/location_form_list_test.ts
@@ -30,45 +30,58 @@ describe("locationFormList()", () => {
label: "Generic tool (100, 200, 300)",
value: "1",
});
- const plantHeading = items[3];
- expect(plantHeading).toEqual({
- headingId: "Plant",
- label: "Plants",
- value: 0,
- heading: true,
- });
- const plant = items[4];
- expect(plant).toEqual({
- headingId: "Plant",
- label: "Plant 1 (1, 2, 3)",
- value: "1"
- });
- const pointHeading = items[6];
- expect(pointHeading).toEqual({
- headingId: "GenericPointer",
- label: "Map Points",
- value: 0,
- heading: true,
- });
- const point = items[7];
- expect(point).toEqual({
- headingId: "GenericPointer",
- label: "Point 1 (10, 20, 30)",
- value: "2"
- });
- const groupHeading = items[8];
+ const groupHeading = items[3];
expect(groupHeading).toEqual({
headingId: "PointGroup",
label: "Groups",
value: 0,
heading: true,
});
- const group = items[9];
+ const group = items[4];
expect(group).toEqual({
headingId: "PointGroup",
label: "Fake",
value: "1"
});
+ const plantHeading = items[5];
+ expect(plantHeading).toEqual({
+ headingId: "Plant",
+ label: "Plants",
+ value: 0,
+ heading: true,
+ });
+ const plant = items[6];
+ expect(plant).toEqual({
+ headingId: "Plant",
+ label: "Plant 1 (1, 2, 3)",
+ value: "1"
+ });
+ const pointHeading = items[8];
+ expect(pointHeading).toEqual({
+ headingId: "GenericPointer",
+ label: "Map Points",
+ value: 0,
+ heading: true,
+ });
+ const point = items[9];
+ expect(point).toEqual({
+ headingId: "GenericPointer",
+ label: "Point 1 (10, 20, 30)",
+ value: "2"
+ });
+ const weedHeading = items[10];
+ expect(weedHeading).toEqual({
+ headingId: "Weed",
+ label: "Weeds",
+ value: 0,
+ heading: true,
+ });
+ const weed = items[11];
+ expect(weed).toEqual({
+ headingId: "Weed",
+ label: "Weed 1 (15, 25, 35)",
+ value: "5"
+ });
});
});
diff --git a/frontend/sequences/locals_list/handle_select.ts b/frontend/sequences/locals_list/handle_select.ts
index af2773181..1f03ac1a6 100644
--- a/frontend/sequences/locals_list/handle_select.ts
+++ b/frontend/sequences/locals_list/handle_select.ts
@@ -78,7 +78,7 @@ const toolVar = (value: string | number) =>
});
const pointVar = (
- pointer_type: "Plant" | "GenericPointer",
+ pointer_type: "Plant" | "GenericPointer" | "Weed",
value: string | number,
) => ({ identifierLabel: label, allowedVariableNodes }: NewVarProps): VariableWithAValue =>
createVariableNode(allowedVariableNodes)(label, {
@@ -123,7 +123,9 @@ const createNewVariable = (props: NewVarProps): VariableNode | undefined => {
if (ddi.isNull) { return nothingVar(props); } // Empty form. Nothing selected yet.
switch (ddi.headingId) {
case "Plant":
- case "GenericPointer": return pointVar(ddi.headingId, ddi.value)(props);
+ case "GenericPointer":
+ case "Weed":
+ return pointVar(ddi.headingId, ddi.value)(props);
case "Tool": return toolVar(ddi.value)(props);
case "parameter": return newParameter(props);
case "Coordinate": return manualEntry(ddi.value)(props);
diff --git a/frontend/sequences/locals_list/location_form_list.ts b/frontend/sequences/locals_list/location_form_list.ts
index e778a8e72..39f90e8c5 100644
--- a/frontend/sequences/locals_list/location_form_list.ts
+++ b/frontend/sequences/locals_list/location_form_list.ts
@@ -72,17 +72,20 @@ export function locationFormList(resources: ResourceIndex,
const allPoints = selectAllActivePoints(resources);
const plantDDI = points2ddi(allPoints, "Plant");
const genericPointerDDI = points2ddi(allPoints, "GenericPointer");
+ const weedDDI = points2ddi(allPoints, "Weed");
const toolDDI = activeToolDDIs(resources);
return [COORDINATE_DDI()]
.concat(additionalItems)
.concat(heading("Tool"))
.concat(toolDDI)
+ .concat(displayGroups ? heading("PointGroup") : [])
+ .concat(displayGroups ? groups2Ddi(selectAllPointGroups(resources)) : [])
.concat(heading("Plant"))
.concat(plantDDI)
.concat(heading("GenericPointer"))
.concat(genericPointerDDI)
- .concat(displayGroups ? heading("PointGroup") : [])
- .concat(displayGroups ? groups2Ddi(selectAllPointGroups(resources)) : []);
+ .concat(heading("Weed"))
+ .concat(weedDDI);
}
/** Create drop down item with label; i.e., "Point/Plant (1, 2, 3)" */
@@ -126,15 +129,6 @@ export function dropDownName(name: string, v?: Record,
return capitalize(label);
}
-export const ALL_POINT_LABELS = {
- "Plant": "All plants",
- "GenericPointer": "All map points",
- "Tool": "All tools and seed containers",
- "ToolSlot": "All slots",
-};
-
-export type EveryPointType = keyof typeof ALL_POINT_LABELS;
-
export const COORDINATE_DDI = (vector?: Vector3): DropDownItem => ({
label: vector
? `${t("Coordinate")} (${vector.x}, ${vector.y}, ${vector.z})`
diff --git a/frontend/sequences/locals_list/test_helpers.ts b/frontend/sequences/locals_list/test_helpers.ts
index 135ca28b1..add4ae492 100644
--- a/frontend/sequences/locals_list/test_helpers.ts
+++ b/frontend/sequences/locals_list/test_helpers.ts
@@ -52,6 +52,16 @@ export function fakeResourceIndex(extra: TaggedResource[] = []): ResourceIndex {
"y": 200,
"z": 300,
}),
+ ...newTaggedResource("Point", {
+ id: 5,
+ meta: {},
+ name: "Weed 1",
+ pointer_type: "Weed",
+ radius: 15,
+ x: 15,
+ y: 25,
+ z: 35,
+ }),
...newTaggedResource("Tool", {
"id": 1,
"name": "Generic Tool",
diff --git a/frontend/sequences/step_tiles/mark_as.tsx b/frontend/sequences/step_tiles/mark_as.tsx
index ccc1d507c..598efeb07 100644
--- a/frontend/sequences/step_tiles/mark_as.tsx
+++ b/frontend/sequences/step_tiles/mark_as.tsx
@@ -11,7 +11,7 @@ import { commitStepChanges } from "./mark_as/commit_step_changes";
import { t } from "../../i18next_wrapper";
interface MarkAsState { nextResource: DropDownItem | undefined }
-const NONE: DropDownItem = { value: 0, label: "" };
+const NONE = (): DropDownItem => ({ label: t("Select one"), value: 0 });
export class MarkAs extends React.Component {
state: MarkAsState = { nextResource: undefined };
@@ -57,7 +57,7 @@ export class MarkAs extends React.Component {
list={actionList(this.state.nextResource, step, this.props.resources)}
onChange={this.commitSelection}
key={JSON.stringify(rightSide) + JSON.stringify(this.state)}
- selectedItem={this.state.nextResource ? NONE : rightSide} />
+ selectedItem={this.state.nextResource ? NONE() : rightSide} />
diff --git a/frontend/sequences/step_tiles/mark_as/__tests__/action_list_test.ts b/frontend/sequences/step_tiles/mark_as/__tests__/action_list_test.ts
index 15e0c97aa..04d6d33c8 100644
--- a/frontend/sequences/step_tiles/mark_as/__tests__/action_list_test.ts
+++ b/frontend/sequences/step_tiles/mark_as/__tests__/action_list_test.ts
@@ -10,7 +10,7 @@ describe("actionList()", () => {
const step = resourceUpdate({ resource_type: "Plant" });
const { index } = markAsResourceFixture();
const result = actionList(undefined, step, index);
- expect(result).toEqual(PLANT_OPTIONS);
+ expect(result).toEqual(PLANT_OPTIONS());
});
it("provides a list of tool mount actions", () => {
@@ -35,6 +35,16 @@ describe("actionList()", () => {
expect(labels).toContain("Removed");
});
+ it("provides a list of weed pointer actions", () => {
+ const ddi = { label: "test case", value: 1, headingId: "Weed" };
+ const step = resourceUpdate({});
+ const { index } = markAsResourceFixture();
+ const result = actionList(ddi, step, index);
+ expect(result.length).toBe(1);
+ const labels = result.map(x => x.label);
+ expect(labels).toContain("Removed");
+ });
+
it("returns an empty list for all other options", () => {
const ddi = { label: "test case", value: 1, headingId: "USB Cables" };
const step = resourceUpdate({});
diff --git a/frontend/sequences/step_tiles/mark_as/__tests__/resource_list_test.ts b/frontend/sequences/step_tiles/mark_as/__tests__/resource_list_test.ts
index df63cfd85..41e882a62 100644
--- a/frontend/sequences/step_tiles/mark_as/__tests__/resource_list_test.ts
+++ b/frontend/sequences/step_tiles/mark_as/__tests__/resource_list_test.ts
@@ -10,5 +10,9 @@ describe("resourceList()", () => {
expect(headings).toContain("Device");
expect(headings).toContain("Plants");
expect(headings).toContain("Points");
+ expect(headings).toContain("Weeds");
+ const weeds = result.filter(x => x.headingId == "Weed");
+ expect(weeds.length).toEqual(2);
+ expect(weeds[1].label).toEqual("weed 1 (200, 400, 0)");
});
});
diff --git a/frontend/sequences/step_tiles/mark_as/__tests__/unpack_step_test.ts b/frontend/sequences/step_tiles/mark_as/__tests__/unpack_step_test.ts
index 8c8f45072..cc2375c36 100644
--- a/frontend/sequences/step_tiles/mark_as/__tests__/unpack_step_test.ts
+++ b/frontend/sequences/step_tiles/mark_as/__tests__/unpack_step_test.ts
@@ -7,6 +7,10 @@ import {
selectAllGenericPointers,
} from "../../../../resources/selectors";
import { DropDownPair } from "../interfaces";
+import { fakeTool } from "../../../../__test_support__/fake_state/resources";
+import {
+ buildResourceIndex,
+} from "../../../../__test_support__/resource_index_builder";
describe("unpackStep()", () => {
function assertGoodness(result: DropDownPair,
action_label: string,
@@ -41,6 +45,23 @@ describe("unpackStep()", () => {
assertGoodness(result, actionLabel, "mounted", label, value);
});
+ it("unpacks valid tool_ids with missing names", () => {
+ const tool = fakeTool();
+ tool.body.id = 1;
+ tool.body.name = undefined;
+ const resourceIndex = buildResourceIndex([tool]).index;
+ const { body } = selectAllTools(resourceIndex)[0];
+ expect(body).toBeTruthy();
+
+ const result = unpackStep({
+ step: resourceUpdate({ label: "mounted_tool_id", value: body.id || NaN }),
+ resourceIndex
+ });
+ const actionLabel = "Mounted to: Untitled Tool";
+ const { label, value } = TOOL_MOUNT();
+ assertGoodness(result, actionLabel, "mounted", label, value);
+ });
+
it("unpacks invalid tool_ids (that may have been valid previously)", () => {
const result = unpackStep({
step: resourceUpdate({ label: "mounted_tool_id", value: Infinity }),
diff --git a/frontend/sequences/step_tiles/mark_as/action_list.ts b/frontend/sequences/step_tiles/mark_as/action_list.ts
index a57281cc1..60225c797 100644
--- a/frontend/sequences/step_tiles/mark_as/action_list.ts
+++ b/frontend/sequences/step_tiles/mark_as/action_list.ts
@@ -16,7 +16,7 @@ const allToolsAsDDI = (i: ResourceIndex) => {
.filter(x => !!x.body.id)
.map(x => {
return {
- label: `${MOUNTED_TO} ${x.body.name}`,
+ label: `${MOUNTED_TO()} ${x.body.name}`,
value: x.body.id || 0
};
});
@@ -25,9 +25,10 @@ const allToolsAsDDI = (i: ResourceIndex) => {
const DEFAULT = "Default";
const ACTION_LIST: Dictionary
= {
- "Device": (i) => [DISMOUNT, ...allToolsAsDDI(i)],
- "Plant": () => PLANT_OPTIONS,
+ "Device": (i) => [DISMOUNT(), ...allToolsAsDDI(i)],
+ "Plant": () => PLANT_OPTIONS(),
"GenericPointer": () => POINT_OPTIONS,
+ "Weed": () => POINT_OPTIONS,
[DEFAULT]: () => []
};
diff --git a/frontend/sequences/step_tiles/mark_as/assertion_support.ts b/frontend/sequences/step_tiles/mark_as/assertion_support.ts
index bb5140a09..289b3be68 100644
--- a/frontend/sequences/step_tiles/mark_as/assertion_support.ts
+++ b/frontend/sequences/step_tiles/mark_as/assertion_support.ts
@@ -7,6 +7,7 @@ import {
fakePlant,
fakePoint,
fakeSequence,
+ fakeWeed,
} from "../../../__test_support__/fake_state/resources";
import { betterMerge } from "../../../util";
import { MarkAs } from "../mark_as";
@@ -30,6 +31,7 @@ export const markAsResourceFixture = () => buildResourceIndex([
fakePlant(),
betterMerge(fakeTool(), { body: { name: "T2", id: 2 } }),
betterMerge(fakePoint(), { body: { name: "my point", id: 7 } }),
+ betterMerge(fakeWeed(), { body: { name: "weed 1", id: 8 } }),
betterMerge(fakeTool(), { body: { name: "T3", id: undefined } }),
]);
diff --git a/frontend/sequences/step_tiles/mark_as/constants.ts b/frontend/sequences/step_tiles/mark_as/constants.ts
index b488bd13c..256ca247d 100644
--- a/frontend/sequences/step_tiles/mark_as/constants.ts
+++ b/frontend/sequences/step_tiles/mark_as/constants.ts
@@ -1,9 +1,11 @@
import { DropDownItem } from "../../../ui";
import { t } from "../../../i18next_wrapper";
+import { PLANT_STAGE_LIST } from "../../../farm_designer/plants/edit_plant_status";
-export const MOUNTED_TO = t("Mounted to:");
+export const MOUNTED_TO = () => t("Mounted to:");
-export const DISMOUNT: DropDownItem = { label: t("Not Mounted"), value: 0 };
+export const DISMOUNT = (): DropDownItem =>
+ ({ label: t("Not Mounted"), value: 0 });
/** Legal "actions" for "Mark As.." block when marking Point resources */
export const POINT_OPTIONS: DropDownItem[] = [
@@ -12,12 +14,7 @@ export const POINT_OPTIONS: DropDownItem[] = [
/** Legal "actions" in the "Mark As.." block when operating on
* a Plant resource. */
-export const PLANT_OPTIONS: DropDownItem[] = [
- { label: t("Planned"), value: "planned" },
- { label: t("Planted"), value: "planted" },
- { label: t("Sprouted"), value: "sprouted" },
- { label: t("Harvested"), value: "harvested" },
-];
+export const PLANT_OPTIONS = PLANT_STAGE_LIST;
const value = 0; // Not used in headings.
@@ -35,6 +32,13 @@ export const POINT_HEADER: DropDownItem = {
heading: true
};
+export const WEED_HEADER: DropDownItem = {
+ headingId: "Weed",
+ label: t("Weeds"),
+ value,
+ heading: true
+};
+
export const TOP_HALF = [
{ headingId: "Device", label: t("Device"), value, heading: true },
{ headingId: "Device", label: t("Tool Mount"), value },
diff --git a/frontend/sequences/step_tiles/mark_as/resource_list.ts b/frontend/sequences/step_tiles/mark_as/resource_list.ts
index 9657fcff8..ecfb606b2 100644
--- a/frontend/sequences/step_tiles/mark_as/resource_list.ts
+++ b/frontend/sequences/step_tiles/mark_as/resource_list.ts
@@ -1,9 +1,9 @@
import { ResourceIndex } from "../../../resources/interfaces";
import { DropDownItem } from "../../../ui/fb_select";
import { selectAllPoints } from "../../../resources/selectors";
-import { TaggedPoint, TaggedPlantPointer } from "farmbot";
-import { GenericPointer } from "farmbot/dist/resources/api_resources";
-import { POINT_HEADER, PLANT_HEADER, TOP_HALF } from "./constants";
+import { TaggedPoint } from "farmbot";
+import { Point } from "farmbot/dist/resources/api_resources";
+import { POINT_HEADER, PLANT_HEADER, TOP_HALF, WEED_HEADER } from "./constants";
/** Filter function to remove resources we don't care about,
* such as ToolSlots and unsaved (Plant|Point)'s */
@@ -17,26 +17,14 @@ const isRelevant = (x: TaggedPoint) => {
const labelStr =
(n: string, x: number, y: number, z: number) => `${n} (${x}, ${y}, ${z})`;
-/** Convert a GenericPointer to a DropDownItem that is formatted appropriately
+/** Convert a Point to a DropDownItem that is formatted appropriately
* for the "Mark As.." step. */
-export const pointer2ddi = (i: GenericPointer): DropDownItem => {
- const { x, y, z, name } = i;
+export const point2ddi = (i: Point): DropDownItem => {
+ const { x, y, z, name, id, pointer_type } = i;
return {
- value: i.id as number,
+ value: id || 0,
label: labelStr(name, x, y, z),
- headingId: "GenericPointer"
- };
-};
-
-/** Convert a PlantPointer to a DropDownItem appropriately formatted for the
- * "Mark As.." step. */
-export const plant2ddi = (i: TaggedPlantPointer["body"]): DropDownItem => {
- const { x, y, z, name, id } = i;
-
- return {
- value: id as number,
- label: labelStr(name, x, y, z),
- headingId: "Plant"
+ headingId: pointer_type,
};
};
@@ -45,16 +33,18 @@ export const plant2ddi = (i: TaggedPlantPointer["body"]): DropDownItem => {
const pointList =
(input: TaggedPoint[]): DropDownItem[] => {
const genericPoints: DropDownItem[] = [POINT_HEADER];
+ const weeds: DropDownItem[] = [WEED_HEADER];
const plants: DropDownItem[] = [PLANT_HEADER];
input
.map(x => x.body)
.forEach(body => {
switch (body.pointer_type) {
- case "GenericPointer": return genericPoints.push(pointer2ddi(body));
- case "Plant": return plants.push(plant2ddi(body));
+ case "GenericPointer": return genericPoints.push(point2ddi(body));
+ case "Weed": return weeds.push(point2ddi(body));
+ case "Plant": return plants.push(point2ddi(body));
}
});
- return [...plants, ...genericPoints];
+ return [...plants, ...genericPoints, ...weeds];
};
/** Creates a formatted DropDownItem list for the "Resource" (left hand) side of
diff --git a/frontend/sequences/step_tiles/mark_as/unpack_step.ts b/frontend/sequences/step_tiles/mark_as/unpack_step.ts
index 7633c2cab..fe04769b7 100644
--- a/frontend/sequences/step_tiles/mark_as/unpack_step.ts
+++ b/frontend/sequences/step_tiles/mark_as/unpack_step.ts
@@ -1,15 +1,12 @@
import { DropDownItem } from "../../../ui";
import {
- findToolById,
- findByKindAndId,
- findPointerByTypeAndId,
+ findToolById, findPointerByTypeAndId,
} from "../../../resources/selectors";
-import { plant2ddi, pointer2ddi } from "./resource_list";
-import { GenericPointer } from "farmbot/dist/resources/api_resources";
+import { point2ddi } from "./resource_list";
import { MOUNTED_TO } from "./constants";
import { DropDownPair, StepWithResourceIndex } from "./interfaces";
-import { TaggedPoint, TaggedPlantPointer } from "farmbot";
import { t } from "../../../i18next_wrapper";
+import { PLANT_STAGE_DDI_LOOKUP } from "../../../farm_designer/plants/edit_plant_status";
export const TOOL_MOUNT = (): DropDownItem => ({
label: t("Tool Mount"), value: "tool_mount"
@@ -19,11 +16,11 @@ export const DISMOUNTED = (): DropDownPair => ({
leftSide: TOOL_MOUNT(),
rightSide: NOT_IN_USE()
});
-const DEFAULT_TOOL_NAME = "Untitled Tool";
-const REMOVED_ACTION = { label: "Removed", value: "removed" };
+const DEFAULT_TOOL_NAME = () => t("Untitled Tool");
+const REMOVED_ACTION = () => ({ label: t("Removed"), value: "removed" });
-const mountedTo = (toolName = DEFAULT_TOOL_NAME): DropDownItem =>
- ({ label: `${MOUNTED_TO} ${toolName}`, value: "mounted" });
+const mountedTo = (toolName = DEFAULT_TOOL_NAME()): DropDownItem =>
+ ({ label: `${MOUNTED_TO()} ${toolName}`, value: "mounted" });
/** The user wants to change the `mounted_tool_id` of their Device. */
function mountTool(i: StepWithResourceIndex): DropDownPair {
@@ -55,23 +52,24 @@ function unknownOption(i: StepWithResourceIndex): DropDownPair {
/** The user wants to mark a the `discarded_at` attribute of a Point. */
function discardPoint(i: StepWithResourceIndex): DropDownPair {
- const { resource_id } = i.step.args;
- const genericPointerBody =
- findPointerByTypeAndId(i.resourceIndex, "GenericPointer", resource_id).body;
+ const { resource_id, resource_type } = i.step.args;
+ const pointerBody =
+ findPointerByTypeAndId(i.resourceIndex, resource_type, resource_id).body;
return {
- leftSide: pointer2ddi(genericPointerBody as GenericPointer),
- rightSide: REMOVED_ACTION
+ leftSide: point2ddi(pointerBody),
+ rightSide: REMOVED_ACTION(),
};
}
/** The user wants to mark a the `plant_stage` attribute of a Plant resource. */
function plantStage(i: StepWithResourceIndex): DropDownPair {
- const { resource_id, value } = i.step.args;
- const r: TaggedPoint = findByKindAndId(i.resourceIndex, "Point", resource_id);
-
+ const { resource_id, resource_type, value } = i.step.args;
+ const pointerBody =
+ findPointerByTypeAndId(i.resourceIndex, resource_type, resource_id).body;
return {
- leftSide: plant2ddi(r.body as TaggedPlantPointer["body"]),
- rightSide: { label: ("" + value), value: ("" + value) }
+ leftSide: point2ddi(pointerBody),
+ rightSide: PLANT_STAGE_DDI_LOOKUP()["" + value]
+ || { label: "" + value, value: "" + value },
};
}
diff --git a/frontend/session_keys.ts b/frontend/session_keys.ts
index 8b6ffb66c..4337f7a21 100644
--- a/frontend/session_keys.ts
+++ b/frontend/session_keys.ts
@@ -17,6 +17,7 @@ export const BooleanSetting: Record = {
legend_menu_open: "legend_menu_open",
show_plants: "show_plants",
show_points: "show_points",
+ show_weeds: "show_weeds",
show_historic_points: "show_historic_points",
show_spread: "show_spread",
show_farmbot: "show_farmbot",
diff --git a/package.json b/package.json
index f3b378bc8..2905deeeb 100644
--- a/package.json
+++ b/package.json
@@ -45,7 +45,7 @@
"coveralls": "3.0.11",
"enzyme": "3.11.0",
"enzyme-adapter-react-16": "1.15.2",
- "farmbot": "9.2.0-rc3",
+ "farmbot": "9.2.3",
"i18next": "19.3.3",
"install": "0.13.0",
"lodash": "4.17.15",
diff --git a/public/app-resources/img/generic-weed.svg b/public/app-resources/img/generic-weed.svg
new file mode 100644
index 000000000..d587a90df
--- /dev/null
+++ b/public/app-resources/img/generic-weed.svg
@@ -0,0 +1,289 @@
+
+
+