commit
0bd6d9a967
|
@ -564,10 +564,10 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.more-bugs,
|
.more-bugs,
|
||||||
|
.select-mode,
|
||||||
.move-to-mode {
|
.move-to-mode {
|
||||||
button {
|
margin: auto;
|
||||||
margin-right: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
|
||||||
p {
|
p {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding-top: 2rem;
|
padding-top: 2rem;
|
||||||
|
|
|
@ -291,7 +291,7 @@
|
||||||
.panel-action-buttons {
|
.panel-action-buttons {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 9;
|
z-index: 9;
|
||||||
height: 19rem;
|
height: 25rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: $panel_medium_light_gray;
|
background: $panel_medium_light_gray;
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
|
@ -303,6 +303,7 @@
|
||||||
min-width: -webkit-fill-available;
|
min-width: -webkit-fill-available;
|
||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
margin-left: .5rem;
|
margin-left: .5rem;
|
||||||
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
.button-row {
|
.button-row {
|
||||||
float: left;
|
float: left;
|
||||||
|
@ -325,7 +326,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.panel-content {
|
.panel-content {
|
||||||
padding-top: 19rem;
|
padding-top: 25rem;
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
padding-bottom: 5rem;
|
padding-bottom: 5rem;
|
||||||
|
|
|
@ -41,6 +41,13 @@ export function Diagnosis(props: DiagnosisProps) {
|
||||||
<div className={"saucer-connector last " + diagnosisColor} />
|
<div className={"saucer-connector last " + diagnosisColor} />
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={10} className={"connectivity-diagnosis"}>
|
<Col xs={10} className={"connectivity-diagnosis"}>
|
||||||
|
<p className="blinking">
|
||||||
|
{t("Always")}
|
||||||
|
<a className="blinking" href="/app/device?highlight=farmbot_os">
|
||||||
|
<u>{t("upgrade FarmBot OS")}</u>
|
||||||
|
</a>
|
||||||
|
{t("before troubleshooting.")}
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{diagnose(props)}
|
{diagnose(props)}
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { BooleanSetting } from "../../../session_keys";
|
||||||
import { DevSettings } from "../../../account/dev/dev_support";
|
import { DevSettings } from "../../../account/dev/dev_support";
|
||||||
import { t } from "../../../i18next_wrapper";
|
import { t } from "../../../i18next_wrapper";
|
||||||
import { Feature } from "../../../devices/interfaces";
|
import { Feature } from "../../../devices/interfaces";
|
||||||
|
import { SelectModeLink } from "../../plants/select_plants";
|
||||||
|
|
||||||
export const ZoomControls = ({ zoom, getConfigValue }: {
|
export const ZoomControls = ({ zoom, getConfigValue }: {
|
||||||
zoom: (value: number) => () => void,
|
zoom: (value: number) => () => void,
|
||||||
|
@ -109,6 +110,7 @@ export function GardenMapLegend(props: GardenMapLegendProps) {
|
||||||
<ZoomControls zoom={props.zoom} getConfigValue={props.getConfigValue} />
|
<ZoomControls zoom={props.zoom} getConfigValue={props.getConfigValue} />
|
||||||
<LayerToggles {...props} />
|
<LayerToggles {...props} />
|
||||||
<MoveModeLink />
|
<MoveModeLink />
|
||||||
|
<SelectModeLink />
|
||||||
<BugsControls />
|
<BugsControls />
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
jest.mock("../../../api/crud", () => ({
|
||||||
|
edit: jest.fn(),
|
||||||
|
save: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import { EditPlantStatusProps } from "../plant_panel";
|
||||||
|
import { shallow } from "enzyme";
|
||||||
|
import {
|
||||||
|
fakePlant, fakeWeed,
|
||||||
|
} from "../../../__test_support__/fake_state/resources";
|
||||||
|
import { edit } from "../../../api/crud";
|
||||||
|
import {
|
||||||
|
EditPlantStatus, PlantStatusBulkUpdateProps, PlantStatusBulkUpdate,
|
||||||
|
EditWeedStatus, EditWeedStatusProps,
|
||||||
|
} from "../edit_plant_status";
|
||||||
|
|
||||||
|
describe("<EditPlantStatus />", () => {
|
||||||
|
const fakeProps = (): EditPlantStatusProps => ({
|
||||||
|
uuid: "Plant.0.0",
|
||||||
|
plantStatus: "planned",
|
||||||
|
updatePlant: jest.fn(),
|
||||||
|
});
|
||||||
|
|
||||||
|
it("changes stage to planted", () => {
|
||||||
|
const p = fakeProps();
|
||||||
|
const wrapper = shallow(<EditPlantStatus {...p} />);
|
||||||
|
wrapper.find("FBSelect").simulate("change", { value: "planted" });
|
||||||
|
expect(p.updatePlant).toHaveBeenCalledWith("Plant.0.0", {
|
||||||
|
plant_stage: "planted",
|
||||||
|
planted_at: expect.stringContaining("Z")
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("changes stage to planned", () => {
|
||||||
|
const p = fakeProps();
|
||||||
|
const wrapper = shallow(<EditPlantStatus {...p} />);
|
||||||
|
wrapper.find("FBSelect").simulate("change", { value: "planned" });
|
||||||
|
expect(p.updatePlant).toHaveBeenCalledWith("Plant.0.0", {
|
||||||
|
plant_stage: "planned",
|
||||||
|
planted_at: undefined
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("<PlantStatusBulkUpdate />", () => {
|
||||||
|
const fakeProps = (): PlantStatusBulkUpdateProps => ({
|
||||||
|
allPoints: [],
|
||||||
|
selected: [],
|
||||||
|
dispatch: jest.fn(),
|
||||||
|
pointerType: "Plant",
|
||||||
|
});
|
||||||
|
|
||||||
|
it("doesn't update plant statuses", () => {
|
||||||
|
const p = fakeProps();
|
||||||
|
const plant1 = fakePlant();
|
||||||
|
const plant2 = fakePlant();
|
||||||
|
p.allPoints = [plant1, plant2];
|
||||||
|
p.selected = [plant1.uuid];
|
||||||
|
const wrapper = shallow(<PlantStatusBulkUpdate {...p} />);
|
||||||
|
window.confirm = jest.fn(() => false);
|
||||||
|
wrapper.find("FBSelect").simulate("change", { label: "", value: "planted" });
|
||||||
|
expect(window.confirm).toHaveBeenCalled();
|
||||||
|
expect(edit).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates plant statuses", () => {
|
||||||
|
const p = fakeProps();
|
||||||
|
const plant1 = fakePlant();
|
||||||
|
const plant2 = fakePlant();
|
||||||
|
const plant3 = fakePlant();
|
||||||
|
p.allPoints = [plant1, plant2, plant3];
|
||||||
|
p.selected = [plant1.uuid, plant2.uuid];
|
||||||
|
const wrapper = shallow(<PlantStatusBulkUpdate {...p} />);
|
||||||
|
window.confirm = jest.fn(() => true);
|
||||||
|
wrapper.find("FBSelect").simulate("change", { label: "", value: "planted" });
|
||||||
|
expect(window.confirm).toHaveBeenCalledWith(
|
||||||
|
"Change status to 'planted' for 2 items?");
|
||||||
|
expect(edit).toHaveBeenCalledTimes(2);
|
||||||
|
expect(edit).toHaveBeenCalledWith(plant1, {
|
||||||
|
plant_stage: "planted",
|
||||||
|
planted_at: expect.stringContaining("Z"),
|
||||||
|
});
|
||||||
|
expect(edit).toHaveBeenCalledWith(plant2, {
|
||||||
|
plant_stage: "planted",
|
||||||
|
planted_at: expect.stringContaining("Z"),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates weed statuses", () => {
|
||||||
|
const p = fakeProps();
|
||||||
|
p.pointerType = "Weed";
|
||||||
|
const weed1 = fakeWeed();
|
||||||
|
const weed2 = fakeWeed();
|
||||||
|
const weed3 = fakeWeed();
|
||||||
|
p.allPoints = [weed1, weed2, weed3];
|
||||||
|
p.selected = [weed1.uuid, weed2.uuid];
|
||||||
|
const wrapper = shallow(<PlantStatusBulkUpdate {...p} />);
|
||||||
|
window.confirm = jest.fn(() => true);
|
||||||
|
wrapper.find("FBSelect").simulate("change", { label: "", value: "removed" });
|
||||||
|
expect(window.confirm).toHaveBeenCalledWith(
|
||||||
|
"Change status to 'removed' for 2 items?");
|
||||||
|
expect(edit).toHaveBeenCalledTimes(2);
|
||||||
|
expect(edit).toHaveBeenCalledWith(weed1, { plant_stage: "removed" });
|
||||||
|
expect(edit).toHaveBeenCalledWith(weed2, { plant_stage: "removed" });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("<EditWeedStatus />", () => {
|
||||||
|
const fakeProps = (): EditWeedStatusProps => ({
|
||||||
|
weed: fakeWeed(),
|
||||||
|
updateWeed: jest.fn(),
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates weed status", () => {
|
||||||
|
const p = fakeProps();
|
||||||
|
const wrapper = shallow(<EditWeedStatus {...p} />);
|
||||||
|
wrapper.find("FBSelect").simulate("change", { label: "", value: "removed" });
|
||||||
|
expect(p.updateWeed).toHaveBeenCalledWith({ plant_stage: "removed" });
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,13 +1,8 @@
|
||||||
jest.mock("../../../history", () => ({ history: { push: jest.fn() } }));
|
jest.mock("../../../history", () => ({ history: { push: jest.fn() } }));
|
||||||
|
|
||||||
jest.mock("../../../api/crud", () => ({
|
|
||||||
edit: jest.fn(),
|
|
||||||
save: jest.fn(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {
|
import {
|
||||||
PlantPanel, PlantPanelProps, EditPlantStatusProps,
|
PlantPanel, PlantPanelProps,
|
||||||
EditDatePlantedProps, EditDatePlanted, EditPlantLocationProps,
|
EditDatePlantedProps, EditDatePlanted, EditPlantLocationProps,
|
||||||
EditPlantLocation,
|
EditPlantLocation,
|
||||||
} from "../plant_panel";
|
} from "../plant_panel";
|
||||||
|
@ -18,11 +13,6 @@ import { clickButton } from "../../../__test_support__/helpers";
|
||||||
import { history } from "../../../history";
|
import { history } from "../../../history";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { fakeTimeSettings } from "../../../__test_support__/fake_time_settings";
|
import { fakeTimeSettings } from "../../../__test_support__/fake_time_settings";
|
||||||
import { fakePlant } from "../../../__test_support__/fake_state/resources";
|
|
||||||
import { edit } from "../../../api/crud";
|
|
||||||
import {
|
|
||||||
EditPlantStatus, PlantStatusBulkUpdateProps, PlantStatusBulkUpdate,
|
|
||||||
} from "../edit_plant_status";
|
|
||||||
|
|
||||||
describe("<PlantPanel/>", () => {
|
describe("<PlantPanel/>", () => {
|
||||||
const info: FormattedPlantInfo = {
|
const info: FormattedPlantInfo = {
|
||||||
|
@ -106,70 +96,6 @@ describe("<PlantPanel/>", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("<EditPlantStatus />", () => {
|
|
||||||
const fakeProps = (): EditPlantStatusProps => ({
|
|
||||||
uuid: "Plant.0.0",
|
|
||||||
plantStatus: "planned",
|
|
||||||
updatePlant: jest.fn(),
|
|
||||||
});
|
|
||||||
|
|
||||||
it("changes stage to planted", () => {
|
|
||||||
const p = fakeProps();
|
|
||||||
const wrapper = shallow(<EditPlantStatus {...p} />);
|
|
||||||
wrapper.find("FBSelect").simulate("change", { value: "planted" });
|
|
||||||
expect(p.updatePlant).toHaveBeenCalledWith("Plant.0.0", {
|
|
||||||
plant_stage: "planted",
|
|
||||||
planted_at: expect.stringContaining("Z")
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("changes stage to planned", () => {
|
|
||||||
const p = fakeProps();
|
|
||||||
const wrapper = shallow(<EditPlantStatus {...p} />);
|
|
||||||
wrapper.find("FBSelect").simulate("change", { value: "planned" });
|
|
||||||
expect(p.updatePlant).toHaveBeenCalledWith("Plant.0.0", {
|
|
||||||
plant_stage: "planned",
|
|
||||||
planted_at: undefined
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("<PlantStatusBulkUpdate />", () => {
|
|
||||||
const fakeProps = (): PlantStatusBulkUpdateProps => ({
|
|
||||||
plants: [],
|
|
||||||
selected: [],
|
|
||||||
dispatch: jest.fn(),
|
|
||||||
});
|
|
||||||
|
|
||||||
it("doesn't update plant statuses", () => {
|
|
||||||
const p = fakeProps();
|
|
||||||
const plant1 = fakePlant();
|
|
||||||
const plant2 = fakePlant();
|
|
||||||
p.plants = [plant1, plant2];
|
|
||||||
p.selected = [plant1.uuid];
|
|
||||||
const wrapper = shallow(<PlantStatusBulkUpdate {...p} />);
|
|
||||||
window.confirm = jest.fn(() => false);
|
|
||||||
wrapper.find("FBSelect").simulate("change", { label: "", value: "planted" });
|
|
||||||
expect(window.confirm).toHaveBeenCalled();
|
|
||||||
expect(edit).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("updates plant statuses", () => {
|
|
||||||
const p = fakeProps();
|
|
||||||
const plant1 = fakePlant();
|
|
||||||
const plant2 = fakePlant();
|
|
||||||
const plant3 = fakePlant();
|
|
||||||
p.plants = [plant1, plant2, plant3];
|
|
||||||
p.selected = [plant1.uuid, plant2.uuid];
|
|
||||||
const wrapper = shallow(<PlantStatusBulkUpdate {...p} />);
|
|
||||||
window.confirm = jest.fn(() => true);
|
|
||||||
wrapper.find("FBSelect").simulate("change", { label: "", value: "planted" });
|
|
||||||
expect(window.confirm).toHaveBeenCalledWith(
|
|
||||||
"Change the plant status to 'planted' for 2 plants?");
|
|
||||||
expect(edit).toHaveBeenCalledTimes(2);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("<EditDatePlanted />", () => {
|
describe("<EditDatePlanted />", () => {
|
||||||
const fakeProps = (): EditDatePlantedProps => ({
|
const fakeProps = (): EditDatePlantedProps => ({
|
||||||
uuid: "Plant.0.0",
|
uuid: "Plant.0.0",
|
||||||
|
|
|
@ -18,12 +18,13 @@ import * as React from "react";
|
||||||
import { mount, shallow } from "enzyme";
|
import { mount, shallow } from "enzyme";
|
||||||
import {
|
import {
|
||||||
RawSelectPlants as SelectPlants, SelectPlantsProps, mapStateToProps,
|
RawSelectPlants as SelectPlants, SelectPlantsProps, mapStateToProps,
|
||||||
getFilteredPoints, GetFilteredPointsProps, validPointTypes,
|
getFilteredPoints, GetFilteredPointsProps, validPointTypes, SelectModeLink,
|
||||||
} from "../select_plants";
|
} from "../select_plants";
|
||||||
import {
|
import {
|
||||||
fakePlant, fakePoint, fakeWeed, fakeToolSlot, fakeTool,
|
fakePlant, fakePoint, fakeWeed, fakeToolSlot, fakeTool,
|
||||||
fakePlantTemplate,
|
fakePlantTemplate,
|
||||||
fakeWebAppConfig,
|
fakeWebAppConfig,
|
||||||
|
fakePointGroup,
|
||||||
} from "../../../__test_support__/fake_state/resources";
|
} from "../../../__test_support__/fake_state/resources";
|
||||||
import { Actions, Content } from "../../../constants";
|
import { Actions, Content } from "../../../constants";
|
||||||
import { clickButton } from "../../../__test_support__/helpers";
|
import { clickButton } from "../../../__test_support__/helpers";
|
||||||
|
@ -35,6 +36,8 @@ import { mockDispatch } from "../../../__test_support__/fake_dispatch";
|
||||||
import {
|
import {
|
||||||
buildResourceIndex,
|
buildResourceIndex,
|
||||||
} from "../../../__test_support__/resource_index_builder";
|
} from "../../../__test_support__/resource_index_builder";
|
||||||
|
import { history } from "../../../history";
|
||||||
|
import { POINTER_TYPES } from "../../point_groups/criteria/interfaces";
|
||||||
|
|
||||||
describe("<SelectPlants />", () => {
|
describe("<SelectPlants />", () => {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
|
@ -60,6 +63,7 @@ describe("<SelectPlants />", () => {
|
||||||
quadrant: 2,
|
quadrant: 2,
|
||||||
isActive: () => false,
|
isActive: () => false,
|
||||||
tools: [],
|
tools: [],
|
||||||
|
groups: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,7 +215,57 @@ describe("<SelectPlants />", () => {
|
||||||
{ payload: undefined, type: Actions.SELECT_POINT });
|
{ payload: undefined, type: Actions.SELECT_POINT });
|
||||||
});
|
});
|
||||||
|
|
||||||
const DELETE_BTN_INDEX = 3;
|
it("selects group items", () => {
|
||||||
|
const p = fakeProps();
|
||||||
|
p.selected = undefined;
|
||||||
|
const group = fakePointGroup();
|
||||||
|
group.body.id = 1;
|
||||||
|
const plant = fakePlant();
|
||||||
|
plant.body.id = 1;
|
||||||
|
group.body.point_ids = [1];
|
||||||
|
p.groups = [group];
|
||||||
|
p.allPoints = [plant];
|
||||||
|
const dispatch = jest.fn();
|
||||||
|
p.dispatch = mockDispatch(dispatch);
|
||||||
|
const wrapper = mount<SelectPlants>(<SelectPlants {...p} />);
|
||||||
|
const actionsWrapper = shallow(wrapper.instance().ActionButtons());
|
||||||
|
expect(wrapper.state().group_id).toEqual(undefined);
|
||||||
|
actionsWrapper.find("FBSelect").at(1).simulate("change", {
|
||||||
|
label: "", value: 1
|
||||||
|
});
|
||||||
|
expect(wrapper.state().group_id).toEqual(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({
|
||||||
|
type: Actions.SET_SELECTION_POINT_TYPE,
|
||||||
|
payload: POINTER_TYPES,
|
||||||
|
});
|
||||||
|
expect(p.dispatch).toHaveBeenLastCalledWith({
|
||||||
|
type: Actions.SELECT_POINT,
|
||||||
|
payload: [plant.uuid],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("selects selection type", () => {
|
||||||
|
const p = fakeProps();
|
||||||
|
const group0 = fakePointGroup();
|
||||||
|
group0.body.id = 0;
|
||||||
|
const group1 = fakePointGroup();
|
||||||
|
group1.body.id = 1;
|
||||||
|
group1.body.criteria.string_eq = { pointer_type: ["Plant"] };
|
||||||
|
p.groups = [group0, group1];
|
||||||
|
const dispatch = jest.fn();
|
||||||
|
p.dispatch = mockDispatch(dispatch);
|
||||||
|
const wrapper = mount<SelectPlants>(<SelectPlants {...p} />);
|
||||||
|
const actionsWrapper = shallow(wrapper.instance().ActionButtons());
|
||||||
|
actionsWrapper.find("FBSelect").at(1).simulate("change", {
|
||||||
|
label: "", value: 1
|
||||||
|
});
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({
|
||||||
|
type: Actions.SET_SELECTION_POINT_TYPE,
|
||||||
|
payload: ["Plant"],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const DELETE_BTN_INDEX = 4;
|
||||||
|
|
||||||
it("confirms deletion of selected plants", () => {
|
it("confirms deletion of selected plants", () => {
|
||||||
const p = fakeProps();
|
const p = fakeProps();
|
||||||
|
@ -344,3 +398,11 @@ describe("validPointTypes()", () => {
|
||||||
expect(validPointTypes(["nope"])).toEqual(undefined);
|
expect(validPointTypes(["nope"])).toEqual(undefined);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("<SelectModeLink />", () => {
|
||||||
|
it("navigates to panel", () => {
|
||||||
|
const wrapper = shallow(<SelectModeLink />);
|
||||||
|
wrapper.find("button").simulate("click");
|
||||||
|
expect(history.push).toHaveBeenCalledWith("/app/designer/plants/select");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -1,27 +1,34 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { FBSelect, DropDownItem } from "../../ui";
|
import { FBSelect, DropDownItem } from "../../ui";
|
||||||
import { PlantOptions } from "../interfaces";
|
import { PlantOptions } from "../interfaces";
|
||||||
import { PlantStage } from "farmbot";
|
import { PlantStage, TaggedWeedPointer, PointType, TaggedPoint } from "farmbot";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { t } from "../../i18next_wrapper";
|
import { t } from "../../i18next_wrapper";
|
||||||
import { TaggedPlant } from "../map/interfaces";
|
|
||||||
import { UUID } from "../../resources/interfaces";
|
import { UUID } from "../../resources/interfaces";
|
||||||
import { edit, save } from "../../api/crud";
|
import { edit, save } from "../../api/crud";
|
||||||
import { EditPlantStatusProps } from "./plant_panel";
|
import { EditPlantStatusProps } from "./plant_panel";
|
||||||
|
import { PlantPointer } from "farmbot/dist/resources/api_resources";
|
||||||
|
|
||||||
export const PLANT_STAGE_DDI_LOOKUP = (): { [x: string]: DropDownItem } => ({
|
export const PLANT_STAGE_DDI_LOOKUP = (): { [x: string]: DropDownItem } => ({
|
||||||
planned: { label: t("Planned"), value: "planned" },
|
planned: { label: t("Planned"), value: "planned" },
|
||||||
planted: { label: t("Planted"), value: "planted" },
|
planted: { label: t("Planted"), value: "planted" },
|
||||||
sprouted: { label: t("Sprouted"), value: "sprouted" },
|
sprouted: { label: t("Sprouted"), value: "sprouted" },
|
||||||
harvested: { label: t("Harvested"), value: "harvested" },
|
harvested: { label: t("Harvested"), value: "harvested" },
|
||||||
|
removed: { label: t("Removed"), value: "removed" },
|
||||||
});
|
});
|
||||||
export const PLANT_STAGE_LIST = () => [
|
export const PLANT_STAGE_LIST = () => [
|
||||||
PLANT_STAGE_DDI_LOOKUP().planned,
|
PLANT_STAGE_DDI_LOOKUP().planned,
|
||||||
PLANT_STAGE_DDI_LOOKUP().planted,
|
PLANT_STAGE_DDI_LOOKUP().planted,
|
||||||
PLANT_STAGE_DDI_LOOKUP().sprouted,
|
PLANT_STAGE_DDI_LOOKUP().sprouted,
|
||||||
PLANT_STAGE_DDI_LOOKUP().harvested,
|
PLANT_STAGE_DDI_LOOKUP().harvested,
|
||||||
|
PLANT_STAGE_DDI_LOOKUP().removed,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const WEED_STATUSES = ["removed"];
|
||||||
|
const WEED_STAGE_DDI_LOOKUP = (): Record<string, DropDownItem> => ({
|
||||||
|
removed: PLANT_STAGE_DDI_LOOKUP().removed,
|
||||||
|
});
|
||||||
|
|
||||||
/** Change `planted_at` value based on `plant_stage` update. */
|
/** Change `planted_at` value based on `plant_stage` update. */
|
||||||
const getUpdateByPlantStage = (plant_stage: PlantStage): PlantOptions => {
|
const getUpdateByPlantStage = (plant_stage: PlantStage): PlantOptions => {
|
||||||
const update: PlantOptions = { plant_stage };
|
const update: PlantOptions = { plant_stage };
|
||||||
|
@ -46,33 +53,55 @@ export function EditPlantStatus(props: EditPlantStatusProps) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PlantStatusBulkUpdateProps {
|
export interface PlantStatusBulkUpdateProps {
|
||||||
plants: TaggedPlant[];
|
allPoints: TaggedPoint[];
|
||||||
selected: UUID[];
|
selected: UUID[];
|
||||||
dispatch: Function;
|
dispatch: Function;
|
||||||
|
pointerType: PointType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Update `plant_stage` for multiple plants at once. */
|
/** Update `plant_stage` for multiple plants at once. */
|
||||||
export const PlantStatusBulkUpdate = (props: PlantStatusBulkUpdateProps) =>
|
export const PlantStatusBulkUpdate = (props: PlantStatusBulkUpdateProps) =>
|
||||||
<div className="plant-status-bulk-update">
|
<div className="plant-status-bulk-update">
|
||||||
<p>{t("update plant status to")}</p>
|
<p>{t("update status to")}</p>
|
||||||
<FBSelect
|
<FBSelect
|
||||||
key={JSON.stringify(props.selected)}
|
key={JSON.stringify(props.selected)}
|
||||||
list={PLANT_STAGE_LIST()}
|
list={PLANT_STAGE_LIST().filter(ddi =>
|
||||||
|
props.pointerType == "Plant" || WEED_STATUSES.includes("" + ddi.value))}
|
||||||
selectedItem={undefined}
|
selectedItem={undefined}
|
||||||
customNullLabel={t("Select a status")}
|
customNullLabel={t("Select a status")}
|
||||||
onChange={ddi => {
|
onChange={ddi => {
|
||||||
const plant_stage = ddi.value as PlantStage;
|
const plant_stage = ddi.value as PlantStage;
|
||||||
const update = getUpdateByPlantStage(plant_stage);
|
const update = props.pointerType == "Plant"
|
||||||
const plants = props.plants.filter(plant =>
|
? getUpdateByPlantStage(plant_stage)
|
||||||
props.selected.includes(plant.uuid)
|
: { plant_stage };
|
||||||
&& plant.kind === "Point"
|
const points = props.allPoints.filter(point =>
|
||||||
&& plant.body.plant_stage != plant_stage);
|
props.selected.includes(point.uuid)
|
||||||
plants.length > 0 && confirm(
|
&& point.kind === "Point"
|
||||||
t("Change the plant status to '{{ status }}' for {{ num }} plants?",
|
&& ["Plant", "Weed"].includes(point.body.pointer_type)
|
||||||
{ status: plant_stage, num: plants.length }))
|
&& (point.body as unknown as PlantPointer).plant_stage != plant_stage);
|
||||||
&& plants.map(plant => {
|
points.length > 0 && confirm(
|
||||||
props.dispatch(edit(plant, update));
|
t("Change status to '{{ status }}' for {{ num }} items?",
|
||||||
props.dispatch(save(plant.uuid));
|
{ status: plant_stage, num: points.length }))
|
||||||
|
&& points.map(point => {
|
||||||
|
props.dispatch(edit(point, update));
|
||||||
|
props.dispatch(save(point.uuid));
|
||||||
});
|
});
|
||||||
}} />
|
}} />
|
||||||
</div>;
|
</div>;
|
||||||
|
|
||||||
|
export interface EditWeedStatusProps {
|
||||||
|
weed: TaggedWeedPointer;
|
||||||
|
updateWeed(update: Partial<TaggedWeedPointer["body"]>): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Select a `plant_stage` for a weed. */
|
||||||
|
export const EditWeedStatus = (props: EditWeedStatusProps) =>
|
||||||
|
<FBSelect
|
||||||
|
list={PLANT_STAGE_LIST().filter(ddi => WEED_STATUSES.includes("" + ddi.value))}
|
||||||
|
selectedItem={WEED_STAGE_DDI_LOOKUP()[(
|
||||||
|
props.weed.body as unknown as PlantPointer).plant_stage]}
|
||||||
|
onChange={ddi =>
|
||||||
|
props.updateWeed({
|
||||||
|
["plant_stage" as keyof TaggedWeedPointer["body"]]:
|
||||||
|
ddi.value as PlantStage
|
||||||
|
})} />;
|
||||||
|
|
|
@ -21,10 +21,11 @@ import {
|
||||||
PointType, TaggedPoint, TaggedGenericPointer, TaggedToolSlotPointer,
|
PointType, TaggedPoint, TaggedGenericPointer, TaggedToolSlotPointer,
|
||||||
TaggedTool,
|
TaggedTool,
|
||||||
TaggedWeedPointer,
|
TaggedWeedPointer,
|
||||||
|
TaggedPointGroup,
|
||||||
} from "farmbot";
|
} from "farmbot";
|
||||||
import { UUID } from "../../resources/interfaces";
|
import { UUID } from "../../resources/interfaces";
|
||||||
import {
|
import {
|
||||||
selectAllActivePoints, selectAllToolSlotPointers, selectAllTools,
|
selectAllActivePoints, selectAllToolSlotPointers, selectAllTools, selectAllPointGroups,
|
||||||
} from "../../resources/selectors";
|
} from "../../resources/selectors";
|
||||||
import { PointInventoryItem } from "../points/point_inventory_item";
|
import { PointInventoryItem } from "../points/point_inventory_item";
|
||||||
import { ToolSlotInventoryItem } from "../tools";
|
import { ToolSlotInventoryItem } from "../tools";
|
||||||
|
@ -37,6 +38,7 @@ import { isActive } from "../tools/edit_tool";
|
||||||
import { uniq } from "lodash";
|
import { uniq } from "lodash";
|
||||||
import { POINTER_TYPES } from "../point_groups/criteria/interfaces";
|
import { POINTER_TYPES } from "../point_groups/criteria/interfaces";
|
||||||
import { WeedInventoryItem } from "../weeds/weed_inventory_item";
|
import { WeedInventoryItem } from "../weeds/weed_inventory_item";
|
||||||
|
import { pointsSelectedByGroup } from "../point_groups/criteria";
|
||||||
|
|
||||||
// tslint:disable-next-line:no-any
|
// tslint:disable-next-line:no-any
|
||||||
export const isPointType = (x: any): x is PointType => POINTER_TYPES.includes(x);
|
export const isPointType = (x: any): x is PointType => POINTER_TYPES.includes(x);
|
||||||
|
@ -82,6 +84,7 @@ export const mapStateToProps = (props: Everything): SelectPlantsProps => {
|
||||||
dispatch: props.dispatch,
|
dispatch: props.dispatch,
|
||||||
gardenOpen: props.resources.consumers.farm_designer.openedSavedGarden,
|
gardenOpen: props.resources.consumers.farm_designer.openedSavedGarden,
|
||||||
tools: selectAllTools(props.resources.index),
|
tools: selectAllTools(props.resources.index),
|
||||||
|
groups: selectAllPointGroups(props.resources.index),
|
||||||
isActive: isActive(selectAllToolSlotPointers(props.resources.index)),
|
isActive: isActive(selectAllToolSlotPointers(props.resources.index)),
|
||||||
xySwap,
|
xySwap,
|
||||||
quadrant,
|
quadrant,
|
||||||
|
@ -100,9 +103,17 @@ export interface SelectPlantsProps {
|
||||||
quadrant: BotOriginQuadrant;
|
quadrant: BotOriginQuadrant;
|
||||||
isActive(id: number | undefined): boolean;
|
isActive(id: number | undefined): boolean;
|
||||||
tools: TaggedTool[];
|
tools: TaggedTool[];
|
||||||
|
groups: TaggedPointGroup[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RawSelectPlants extends React.Component<SelectPlantsProps, {}> {
|
interface SelectPlantsState {
|
||||||
|
group_id: number | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RawSelectPlants
|
||||||
|
extends React.Component<SelectPlantsProps, SelectPlantsState> {
|
||||||
|
state: SelectPlantsState = { group_id: undefined };
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { dispatch, selected } = this.props;
|
const { dispatch, selected } = this.props;
|
||||||
if (selected && selected.length == 1) {
|
if (selected && selected.length == 1) {
|
||||||
|
@ -135,13 +146,39 @@ export class RawSelectPlants extends React.Component<SelectPlantsProps, {}> {
|
||||||
return selectionPointTypes.length > 1 ? "All" : selectionPointTypes[0];
|
return selectionPointTypes.length > 1 ? "All" : selectionPointTypes[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get groupDDILookup(): Record<number, DropDownItem> {
|
||||||
|
const lookup: Record<number, DropDownItem> = {};
|
||||||
|
this.props.groups.map(group => {
|
||||||
|
const { id } = group.body;
|
||||||
|
const groupName = group.body.name;
|
||||||
|
const count = pointsSelectedByGroup(group, this.props.allPoints).length;
|
||||||
|
const label = `${groupName} (${t("{{count}} items", { count })})`;
|
||||||
|
id && (lookup[id] = { label, value: id });
|
||||||
|
});
|
||||||
|
return lookup;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectGroup = (ddi: DropDownItem) => {
|
||||||
|
const group_id = parseInt("" + ddi.value);
|
||||||
|
this.setState({ group_id });
|
||||||
|
const group = this.props.groups
|
||||||
|
.filter(pg => pg.body.id == group_id)[0];
|
||||||
|
const pointUuids = pointsSelectedByGroup(group, this.props.allPoints)
|
||||||
|
.map(p => p.uuid);
|
||||||
|
const pointerTypes =
|
||||||
|
group.body.criteria.string_eq.pointer_type as PointType[] | undefined;
|
||||||
|
this.props.dispatch(setSelectionPointType(pointerTypes || POINTER_TYPES));
|
||||||
|
this.props.dispatch(selectPoint(pointUuids));
|
||||||
|
}
|
||||||
|
|
||||||
ActionButtons = () =>
|
ActionButtons = () =>
|
||||||
<div className="panel-action-buttons">
|
<div className="panel-action-buttons">
|
||||||
<FBSelect
|
<FBSelect key={this.selectionPointType}
|
||||||
list={POINTER_TYPE_LIST()}
|
list={POINTER_TYPE_LIST()}
|
||||||
selectedItem={POINTER_TYPE_DDI_LOOKUP()[this.selectionPointType]}
|
selectedItem={POINTER_TYPE_DDI_LOOKUP()[this.selectionPointType]}
|
||||||
onChange={ddi => {
|
onChange={ddi => {
|
||||||
this.props.dispatch(selectPoint(undefined));
|
this.props.dispatch(selectPoint(undefined));
|
||||||
|
this.setState({ group_id: undefined });
|
||||||
this.props.dispatch(setSelectionPointType(
|
this.props.dispatch(setSelectionPointType(
|
||||||
ddi.value == "All" ? POINTER_TYPES : validPointTypes([ddi.value])));
|
ddi.value == "All" ? POINTER_TYPES : validPointTypes([ddi.value])));
|
||||||
}} />
|
}} />
|
||||||
|
@ -156,6 +193,14 @@ export class RawSelectPlants extends React.Component<SelectPlantsProps, {}> {
|
||||||
onClick={() => this.props.dispatch(selectPoint(this.allPointUuids))}>
|
onClick={() => this.props.dispatch(selectPoint(this.allPointUuids))}>
|
||||||
{t("Select all")}
|
{t("Select all")}
|
||||||
</button>
|
</button>
|
||||||
|
<label>{t("select all in group")}</label>
|
||||||
|
<FBSelect key={this.selectionPointType}
|
||||||
|
list={Object.values(this.groupDDILookup)}
|
||||||
|
selectedItem={this.state.group_id
|
||||||
|
? this.groupDDILookup[this.state.group_id]
|
||||||
|
: undefined}
|
||||||
|
customNullLabel={t("Select a group")}
|
||||||
|
onChange={this.selectGroup} />
|
||||||
</div>
|
</div>
|
||||||
<label>{t("SELECTION ACTIONS")}</label>
|
<label>{t("SELECTION ACTIONS")}</label>
|
||||||
<div className="button-row">
|
<div className="button-row">
|
||||||
|
@ -171,9 +216,10 @@ export class RawSelectPlants extends React.Component<SelectPlantsProps, {}> {
|
||||||
: error(t(Content.ERROR_PLANT_TEMPLATE_GROUP))}>
|
: error(t(Content.ERROR_PLANT_TEMPLATE_GROUP))}>
|
||||||
{t("Create group")}
|
{t("Create group")}
|
||||||
</button>
|
</button>
|
||||||
{this.selectionPointType == "Plant" &&
|
{(this.selectionPointType == "Plant" || this.selectionPointType == "Weed") &&
|
||||||
<PlantStatusBulkUpdate
|
<PlantStatusBulkUpdate
|
||||||
plants={this.props.plants}
|
pointerType={this.selectionPointType}
|
||||||
|
allPoints={this.props.allPoints}
|
||||||
selected={this.selected}
|
selected={this.selected}
|
||||||
dispatch={this.props.dispatch} />}
|
dispatch={this.props.dispatch} />}
|
||||||
</div>
|
</div>
|
||||||
|
@ -320,3 +366,13 @@ const getVisibleLayers = (getConfigValue: GetWebAppConfigValue): PointType[] =>
|
||||||
...(showFarmbot ? [PointerType.ToolSlot] : []),
|
...(showFarmbot ? [PointerType.ToolSlot] : []),
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const SelectModeLink = () =>
|
||||||
|
<div className="select-mode">
|
||||||
|
<button
|
||||||
|
className="fb-button gray"
|
||||||
|
title={t("open point select panel")}
|
||||||
|
onClick={() => history.push("/app/designer/plants/select")}>
|
||||||
|
{t("select")}
|
||||||
|
</button>
|
||||||
|
</div>;
|
||||||
|
|
|
@ -199,7 +199,7 @@ describe("togglePointTypeCriteria()", () => {
|
||||||
const group = fakePointGroup();
|
const group = fakePointGroup();
|
||||||
group.body.criteria.string_eq = {
|
group.body.criteria.string_eq = {
|
||||||
pointer_type: ["Plant", "ToolSlot"],
|
pointer_type: ["Plant", "ToolSlot"],
|
||||||
"plant_stage": ["planned"],
|
"openfarm_slug": ["mint"],
|
||||||
};
|
};
|
||||||
const expectedBody = cloneDeep(group.body);
|
const expectedBody = cloneDeep(group.body);
|
||||||
expectedBody.criteria.string_eq = { pointer_type: ["Weed"] };
|
expectedBody.criteria.string_eq = { pointer_type: ["Weed"] };
|
||||||
|
|
|
@ -107,7 +107,7 @@ describe("typeDisabled()", () => {
|
||||||
|
|
||||||
it("isn't disabled", () => {
|
it("isn't disabled", () => {
|
||||||
const criteria = fakeCriteria();
|
const criteria = fakeCriteria();
|
||||||
criteria.string_eq = { plant_stage: ["planted"] };
|
criteria.string_eq = { openfarm_slug: ["mint"] };
|
||||||
const result = typeDisabled(criteria, "Plant");
|
const result = typeDisabled(criteria, "Plant");
|
||||||
expect(result).toBeFalsy();
|
expect(result).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
|
@ -161,7 +161,6 @@ describe("<NumberLtGtInput />", () => {
|
||||||
p.group,
|
p.group,
|
||||||
"number_gt",
|
"number_gt",
|
||||||
"x",
|
"x",
|
||||||
undefined,
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -175,7 +174,6 @@ describe("<NumberLtGtInput />", () => {
|
||||||
p.group,
|
p.group,
|
||||||
"number_lt",
|
"number_lt",
|
||||||
"x",
|
"x",
|
||||||
undefined,
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -81,6 +81,6 @@ describe("<CheckboxList />", () => {
|
||||||
expect(wrapper.text()).toContain("label");
|
expect(wrapper.text()).toContain("label");
|
||||||
wrapper.find("input").first().simulate("change");
|
wrapper.find("input").first().simulate("change");
|
||||||
expect(toggleAndEditEqCriteria).toHaveBeenCalledWith(
|
expect(toggleAndEditEqCriteria).toHaveBeenCalledWith(
|
||||||
p.group, "openfarm_slug", "value", "Plant");
|
p.group, "openfarm_slug", "value");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -60,7 +60,7 @@ export const toggleAndEditEqCriteria = <T extends string | number>(
|
||||||
const wasOff = !tempEqCriteria[key]?.includes(value);
|
const wasOff = !tempEqCriteria[key]?.includes(value);
|
||||||
toggleEqCriteria<T>(tempEqCriteria)(key, value);
|
toggleEqCriteria<T>(tempEqCriteria)(key, value);
|
||||||
pointerType && wasOff && clearSubCriteria(
|
pointerType && wasOff && clearSubCriteria(
|
||||||
POINTER_TYPES.filter(x => x != pointerType), tempCriteria);
|
POINTER_TYPES.filter(x => x != pointerType), tempCriteria, key);
|
||||||
dispatch(editCriteria(group, tempCriteria));
|
dispatch(editCriteria(group, tempCriteria));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -68,16 +68,17 @@ export const toggleAndEditEqCriteria = <T extends string | number>(
|
||||||
export const clearSubCriteria = (
|
export const clearSubCriteria = (
|
||||||
pointerTypes: PointerType[],
|
pointerTypes: PointerType[],
|
||||||
tempCriteria: PointGroupCriteria,
|
tempCriteria: PointGroupCriteria,
|
||||||
|
keepKey: string,
|
||||||
) => {
|
) => {
|
||||||
const toggleStrEq = toggleEqCriteria<string>(tempCriteria.string_eq, "off");
|
const toggleStrEq = toggleEqCriteria<string>(tempCriteria.string_eq, "off");
|
||||||
const toggleNumEq = toggleEqCriteria<number>(tempCriteria.number_eq, "off");
|
const toggleNumEq = toggleEqCriteria<number>(tempCriteria.number_eq, "off");
|
||||||
const toggleStrEqMapper = (key: string) =>
|
const toggleStrEqMapper = (key: string) => key != keepKey &&
|
||||||
tempCriteria.string_eq[key]?.map(value => toggleStrEq(key, value));
|
tempCriteria.string_eq[key]?.map(value => toggleStrEq(key, value));
|
||||||
if (pointerTypes.includes("Plant")) {
|
if (pointerTypes.includes("Plant")) {
|
||||||
["openfarm_slug", "plant_stage"].map(toggleStrEqMapper);
|
["openfarm_slug", "plant_stage"].map(toggleStrEqMapper);
|
||||||
}
|
}
|
||||||
if (pointerTypes.includes("Weed")) {
|
if (pointerTypes.includes("Weed")) {
|
||||||
["meta.created_by"].map(toggleStrEqMapper);
|
["meta.created_by", "plant_stage"].map(toggleStrEqMapper);
|
||||||
}
|
}
|
||||||
if (pointerTypes.includes("GenericPointer") && pointerTypes.includes("Weed")) {
|
if (pointerTypes.includes("GenericPointer") && pointerTypes.includes("Weed")) {
|
||||||
["meta.color"].map(toggleStrEqMapper);
|
["meta.color"].map(toggleStrEqMapper);
|
||||||
|
@ -101,8 +102,8 @@ export const togglePointTypeCriteria =
|
||||||
const toggle = toggleEqCriteria<string>(tempCriteria.string_eq);
|
const toggle = toggleEqCriteria<string>(tempCriteria.string_eq);
|
||||||
clear && (tempCriteria.string_eq.pointer_type = []);
|
clear && (tempCriteria.string_eq.pointer_type = []);
|
||||||
toggle("pointer_type", pointerType);
|
toggle("pointer_type", pointerType);
|
||||||
clearSubCriteria(
|
clearSubCriteria(POINTER_TYPES.filter(x => x != pointerType),
|
||||||
POINTER_TYPES.filter(x => x != pointerType), tempCriteria);
|
tempCriteria, "pointer_type");
|
||||||
dispatch(editCriteria(group, tempCriteria));
|
dispatch(editCriteria(group, tempCriteria));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -164,7 +165,7 @@ export const editGtLtCriteriaField = (
|
||||||
(dispatch: Function) => {
|
(dispatch: Function) => {
|
||||||
const tempCriteria = cloneDeep(group.body.criteria);
|
const tempCriteria = cloneDeep(group.body.criteria);
|
||||||
pointerType && clearSubCriteria(
|
pointerType && clearSubCriteria(
|
||||||
POINTER_TYPES.filter(x => x != pointerType), tempCriteria);
|
POINTER_TYPES.filter(x => x != pointerType), tempCriteria, criteriaKey);
|
||||||
const value = e.currentTarget.value != ""
|
const value = e.currentTarget.value != ""
|
||||||
? parseInt(e.currentTarget.value)
|
? parseInt(e.currentTarget.value)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
|
@ -62,6 +62,7 @@ export const hasSubCriteria = (criteria: PointGroupCriteria) =>
|
||||||
case "Weed":
|
case "Weed":
|
||||||
return !!(
|
return !!(
|
||||||
selected("meta.created_by")
|
selected("meta.created_by")
|
||||||
|
|| selected("plant_stage")
|
||||||
|| selected("meta.color")
|
|| selected("meta.color")
|
||||||
|| numSelected("radius"));
|
|| numSelected("radius"));
|
||||||
case "Plant":
|
case "Plant":
|
||||||
|
|
|
@ -153,7 +153,7 @@ export const DaySelection = (props: DaySelectionProps) => {
|
||||||
|
|
||||||
/** Edit number < and > criteria. */
|
/** Edit number < and > criteria. */
|
||||||
export const NumberLtGtInput = (props: NumberLtGtInputProps) => {
|
export const NumberLtGtInput = (props: NumberLtGtInputProps) => {
|
||||||
const { group, dispatch, criteriaKey, pointerType } = props;
|
const { group, dispatch, criteriaKey } = props;
|
||||||
const gtCriteria = props.group.body.criteria.number_gt;
|
const gtCriteria = props.group.body.criteria.number_gt;
|
||||||
const ltCriteria = props.group.body.criteria.number_lt;
|
const ltCriteria = props.group.body.criteria.number_lt;
|
||||||
return <Row>
|
return <Row>
|
||||||
|
@ -164,7 +164,7 @@ export const NumberLtGtInput = (props: NumberLtGtInputProps) => {
|
||||||
defaultValue={gtCriteria[criteriaKey]}
|
defaultValue={gtCriteria[criteriaKey]}
|
||||||
disabled={props.disabled}
|
disabled={props.disabled}
|
||||||
onBlur={e => dispatch(editGtLtCriteriaField(
|
onBlur={e => dispatch(editGtLtCriteriaField(
|
||||||
group, "number_gt", criteriaKey, pointerType)(e))} />
|
group, "number_gt", criteriaKey)(e))} />
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={1}>
|
<Col xs={1}>
|
||||||
<p>{"<"}</p>
|
<p>{"<"}</p>
|
||||||
|
@ -182,7 +182,7 @@ export const NumberLtGtInput = (props: NumberLtGtInputProps) => {
|
||||||
defaultValue={ltCriteria[criteriaKey]}
|
defaultValue={ltCriteria[criteriaKey]}
|
||||||
disabled={props.disabled}
|
disabled={props.disabled}
|
||||||
onBlur={e => dispatch(editGtLtCriteriaField(
|
onBlur={e => dispatch(editGtLtCriteriaField(
|
||||||
group, "number_lt", criteriaKey, pointerType)(e))} />
|
group, "number_lt", criteriaKey)(e))} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>;
|
</Row>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {
|
||||||
SubCriteriaSectionProps,
|
SubCriteriaSectionProps,
|
||||||
CheckboxListItem,
|
CheckboxListItem,
|
||||||
} from "./interfaces";
|
} from "./interfaces";
|
||||||
import { PLANT_STAGE_LIST } from "../../plants/edit_plant_status";
|
import { PLANT_STAGE_LIST, WEED_STATUSES } from "../../plants/edit_plant_status";
|
||||||
import { DIRECTION_CHOICES } from "../../tools/tool_slot_edit_components";
|
import { DIRECTION_CHOICES } from "../../tools/tool_slot_edit_components";
|
||||||
import { Checkbox } from "../../../ui";
|
import { Checkbox } from "../../../ui";
|
||||||
import { PointType } from "farmbot";
|
import { PointType } from "farmbot";
|
||||||
|
@ -80,7 +80,7 @@ export const CheckboxList =
|
||||||
<div className="criteria-checkbox-list-item" key={index}>
|
<div className="criteria-checkbox-list-item" key={index}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
onChange={() => props.dispatch(toggle<T>(
|
onChange={() => props.dispatch(toggle<T>(
|
||||||
props.group, props.criteriaKey, value, props.pointerType))}
|
props.group, props.criteriaKey, value))}
|
||||||
checked={selected(props.criteriaKey, value)}
|
checked={selected(props.criteriaKey, value)}
|
||||||
title={t(label)}
|
title={t(label)}
|
||||||
color={color}
|
color={color}
|
||||||
|
@ -95,14 +95,16 @@ export const PlantCriteria = (props: PlantSubCriteriaProps) => {
|
||||||
const { group, dispatch, disabled } = props;
|
const { group, dispatch, disabled } = props;
|
||||||
const commonProps = { group, dispatch, disabled };
|
const commonProps = { group, dispatch, disabled };
|
||||||
return <div className={"plant-criteria-options"}>
|
return <div className={"plant-criteria-options"}>
|
||||||
<PlantStage {...commonProps} />
|
<PlantStage {...commonProps} pointerType={"Plant"} />
|
||||||
<PlantType {...commonProps} slugs={props.slugs} />
|
<PlantType {...commonProps} slugs={props.slugs} />
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const PlantStage = (props: SubCriteriaProps) =>
|
const PlantStage = (props: PointSubCriteriaProps) =>
|
||||||
<div className={"plant-stage-criteria"}>
|
<div className={"plant-stage-criteria"}>
|
||||||
<p className={"category"}>{t("Stage")}</p>
|
<p className={"category"}>
|
||||||
|
{props.pointerType == "Plant" ? t("Stage") : t("Status")}
|
||||||
|
</p>
|
||||||
<ClearCategory
|
<ClearCategory
|
||||||
group={props.group}
|
group={props.group}
|
||||||
criteriaCategories={["string_eq"]}
|
criteriaCategories={["string_eq"]}
|
||||||
|
@ -110,12 +112,15 @@ const PlantStage = (props: SubCriteriaProps) =>
|
||||||
dispatch={props.dispatch} />
|
dispatch={props.dispatch} />
|
||||||
<CheckboxList<string>
|
<CheckboxList<string>
|
||||||
disabled={props.disabled}
|
disabled={props.disabled}
|
||||||
pointerType={"Plant"}
|
pointerType={props.pointerType}
|
||||||
criteriaKey={"plant_stage"}
|
criteriaKey={"plant_stage"}
|
||||||
group={props.group}
|
group={props.group}
|
||||||
dispatch={props.dispatch}
|
dispatch={props.dispatch}
|
||||||
list={PLANT_STAGE_LIST().map(ddi =>
|
list={PLANT_STAGE_LIST().filter(ddi =>
|
||||||
({ label: ddi.label, value: "" + ddi.value }))} />
|
props.pointerType == "Plant" || WEED_STATUSES.includes("" + ddi.value))
|
||||||
|
.map(ddi => ({ label: ddi.label, value: "" + ddi.value }))
|
||||||
|
.concat(props.pointerType == "Weed"
|
||||||
|
? [{ label: t("Remaining"), value: "planned" }] : [])} />
|
||||||
</div>;
|
</div>;
|
||||||
|
|
||||||
const PlantType = (props: PlantSubCriteriaProps) =>
|
const PlantType = (props: PlantSubCriteriaProps) =>
|
||||||
|
@ -145,6 +150,7 @@ export const WeedCriteria = (props: SubCriteriaProps) => {
|
||||||
const commonProps = { group, dispatch, disabled, pointerType };
|
const commonProps = { group, dispatch, disabled, pointerType };
|
||||||
return <div className={"weed-criteria-options"}>
|
return <div className={"weed-criteria-options"}>
|
||||||
<PointSource {...commonProps} />
|
<PointSource {...commonProps} />
|
||||||
|
<PlantStage {...commonProps} />
|
||||||
<Color {...commonProps} />
|
<Color {...commonProps} />
|
||||||
<Radius {...commonProps} />
|
<Radius {...commonProps} />
|
||||||
</div>;
|
</div>;
|
||||||
|
|
|
@ -11,9 +11,11 @@ import {
|
||||||
EditPointColor, EditPointColorProps, updatePoint, EditPointName,
|
EditPointColor, EditPointColorProps, updatePoint, EditPointName,
|
||||||
EditPointNameProps,
|
EditPointNameProps,
|
||||||
AdditionalWeedProperties,
|
AdditionalWeedProperties,
|
||||||
EditPointPropertiesProps,
|
AdditionalWeedPropertiesProps,
|
||||||
} from "../point_edit_actions";
|
} from "../point_edit_actions";
|
||||||
import { fakePoint } from "../../../__test_support__/fake_state/resources";
|
import {
|
||||||
|
fakePoint, fakeWeed,
|
||||||
|
} from "../../../__test_support__/fake_state/resources";
|
||||||
import { edit, save } from "../../../api/crud";
|
import { edit, save } from "../../../api/crud";
|
||||||
|
|
||||||
describe("updatePoint()", () => {
|
describe("updatePoint()", () => {
|
||||||
|
@ -94,8 +96,8 @@ describe("<EditPointColor />", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("<AdditionalWeedProperties />", () => {
|
describe("<AdditionalWeedProperties />", () => {
|
||||||
const fakeProps = (): EditPointPropertiesProps => ({
|
const fakeProps = (): AdditionalWeedPropertiesProps => ({
|
||||||
point: fakePoint(),
|
point: fakeWeed(),
|
||||||
updatePoint: jest.fn(),
|
updatePoint: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { Row, Col, BlurableInput, ColorPicker } from "../../ui";
|
||||||
import { parseIntInput } from "../../util";
|
import { parseIntInput } from "../../util";
|
||||||
import { UUID } from "../../resources/interfaces";
|
import { UUID } from "../../resources/interfaces";
|
||||||
import { plantAge } from "../plants/map_state_to_props";
|
import { plantAge } from "../plants/map_state_to_props";
|
||||||
|
import { EditWeedStatus } from "../plants/edit_plant_status";
|
||||||
|
|
||||||
type PointUpdate =
|
type PointUpdate =
|
||||||
Partial<TaggedGenericPointer["body"] | TaggedWeedPointer["body"]>;
|
Partial<TaggedGenericPointer["body"] | TaggedWeedPointer["body"]>;
|
||||||
|
@ -29,6 +30,11 @@ export interface EditPointPropertiesProps {
|
||||||
updatePoint(update: PointUpdate): void;
|
updatePoint(update: PointUpdate): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AdditionalWeedPropertiesProps {
|
||||||
|
point: TaggedWeedPointer;
|
||||||
|
updatePoint(update: PointUpdate): void;
|
||||||
|
}
|
||||||
|
|
||||||
export const EditPointProperties = (props: EditPointPropertiesProps) =>
|
export const EditPointProperties = (props: EditPointPropertiesProps) =>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
|
@ -53,11 +59,14 @@ export const EditPointProperties = (props: EditPointPropertiesProps) =>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</ul>;
|
</ul>;
|
||||||
|
|
||||||
export const AdditionalWeedProperties = (props: EditPointPropertiesProps) =>
|
export const AdditionalWeedProperties = (props: AdditionalWeedPropertiesProps) =>
|
||||||
<ul className="additional-weed-properties">
|
<ul className="additional-weed-properties">
|
||||||
<ListItem name={t("Age")}>
|
<ListItem name={t("Age")}>
|
||||||
{`${plantAge(props.point)} ${t("days old")}`}
|
{`${plantAge(props.point)} ${t("days old")}`}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
<ListItem name={t("Status")}>
|
||||||
|
<EditWeedStatus weed={props.point} updateWeed={props.updatePoint} />
|
||||||
|
</ListItem>
|
||||||
{Object.entries(props.point.body.meta).map(([key, value]) => {
|
{Object.entries(props.point.body.meta).map(([key, value]) => {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case "color":
|
case "color":
|
||||||
|
|
|
@ -158,7 +158,7 @@ export function renderCeleryNode(props: StepParams) {
|
||||||
case "reboot": return <TileReboot {...props} />;
|
case "reboot": return <TileReboot {...props} />;
|
||||||
case "emergency_lock": return <TileEmergencyStop {...props} />;
|
case "emergency_lock": return <TileEmergencyStop {...props} />;
|
||||||
case "assertion": return <TileAssertion {...props} />;
|
case "assertion": return <TileAssertion {...props} />;
|
||||||
case "sync": case "dump_info": case "power_off": case "read_status":
|
case "sync": case "power_off": case "read_status":
|
||||||
case "emergency_unlock": case "install_first_party_farmware":
|
case "emergency_unlock": case "install_first_party_farmware":
|
||||||
return <TileSystemAction {...props} />;
|
return <TileSystemAction {...props} />;
|
||||||
case "check_updates": case "factory_reset":
|
case "check_updates": case "factory_reset":
|
||||||
|
|
|
@ -25,7 +25,6 @@ function translate(input: Step): string {
|
||||||
"wait": t("Wait"),
|
"wait": t("Wait"),
|
||||||
"write_pin": t("Control Peripheral"),
|
"write_pin": t("Control Peripheral"),
|
||||||
"sync": t("Sync"),
|
"sync": t("Sync"),
|
||||||
"dump_info": t("Diagnostic Report"),
|
|
||||||
"power_off": t("Shutdown"),
|
"power_off": t("Shutdown"),
|
||||||
"read_status": t("Read status"),
|
"read_status": t("Read status"),
|
||||||
"emergency_unlock": t("UNLOCK"),
|
"emergency_unlock": t("UNLOCK"),
|
||||||
|
|
Loading…
Reference in New Issue