Merge pull request #1604 from gabrielburnworth/staging
Add saved gardens panelpull/1609/head v8.2.3
commit
e8c6257a7f
|
@ -19,7 +19,7 @@ module Api
|
|||
end
|
||||
|
||||
def snapshot
|
||||
mutate SavedGardens::Snapshot.run(device: current_device)
|
||||
mutate SavedGardens::Snapshot.run(raw_json, device: current_device)
|
||||
end
|
||||
|
||||
def apply
|
||||
|
|
|
@ -17,6 +17,7 @@ import { shallow } from "enzyme";
|
|||
describe("<DesignerNavTabs />", () => {
|
||||
it("renders for map", () => {
|
||||
mockPath = "/app/designer";
|
||||
mockDev = false;
|
||||
const wrapper = shallow(<DesignerNavTabs />);
|
||||
expect(wrapper.hasClass("gray-panel")).toBeTruthy();
|
||||
expect(wrapper.html()).toContain("active");
|
||||
|
@ -24,6 +25,7 @@ describe("<DesignerNavTabs />", () => {
|
|||
|
||||
it("renders for plants", () => {
|
||||
mockPath = "/app/designer/plants";
|
||||
mockDev = false;
|
||||
const wrapper = shallow(<DesignerNavTabs />);
|
||||
expect(wrapper.hasClass("green-panel")).toBeTruthy();
|
||||
expect(wrapper.html()).toContain("active");
|
||||
|
@ -31,6 +33,7 @@ describe("<DesignerNavTabs />", () => {
|
|||
|
||||
it("renders for farm events", () => {
|
||||
mockPath = "/app/designer/events";
|
||||
mockDev = false;
|
||||
const wrapper = shallow(<DesignerNavTabs />);
|
||||
expect(wrapper.hasClass("yellow-panel")).toBeTruthy();
|
||||
expect(wrapper.html()).toContain("active");
|
||||
|
@ -38,7 +41,7 @@ describe("<DesignerNavTabs />", () => {
|
|||
|
||||
it("renders for saved gardens", () => {
|
||||
mockPath = "/app/designer/gardens";
|
||||
mockDev = true;
|
||||
mockDev = false;
|
||||
const wrapper = shallow(<DesignerNavTabs />);
|
||||
expect(wrapper.hasClass("navy-panel")).toBeTruthy();
|
||||
expect(wrapper.html()).toContain("active");
|
||||
|
@ -46,7 +49,7 @@ describe("<DesignerNavTabs />", () => {
|
|||
|
||||
it("renders for points", () => {
|
||||
mockPath = "/app/designer/points";
|
||||
mockDev = true;
|
||||
mockDev = false;
|
||||
const wrapper = shallow(<DesignerNavTabs />);
|
||||
expect(wrapper.hasClass("teal-panel")).toBeTruthy();
|
||||
expect(wrapper.html()).toContain("active");
|
||||
|
@ -54,7 +57,7 @@ describe("<DesignerNavTabs />", () => {
|
|||
|
||||
it("renders for groups", () => {
|
||||
mockPath = "/app/designer/groups";
|
||||
mockDev = true;
|
||||
mockDev = false;
|
||||
const wrapper = shallow(<DesignerNavTabs />);
|
||||
expect(wrapper.hasClass("blue-panel")).toBeTruthy();
|
||||
expect(wrapper.html()).toContain("active");
|
||||
|
@ -62,7 +65,7 @@ describe("<DesignerNavTabs />", () => {
|
|||
|
||||
it("renders for weeds", () => {
|
||||
mockPath = "/app/designer/weeds";
|
||||
mockDev = true;
|
||||
mockDev = false;
|
||||
const wrapper = shallow(<DesignerNavTabs />);
|
||||
expect(wrapper.hasClass("red-panel")).toBeTruthy();
|
||||
expect(wrapper.html()).toContain("active");
|
||||
|
@ -78,7 +81,7 @@ describe("<DesignerNavTabs />", () => {
|
|||
|
||||
it("renders for settings", () => {
|
||||
mockPath = "/app/designer/settings";
|
||||
mockDev = true;
|
||||
mockDev = false;
|
||||
const wrapper = shallow(<DesignerNavTabs />);
|
||||
expect(wrapper.hasClass("gray-panel")).toBeTruthy();
|
||||
expect(wrapper.html()).toContain("active");
|
||||
|
|
|
@ -118,11 +118,10 @@ export function DesignerNavTabs(props: { hidden?: boolean }) {
|
|||
panel={Panel.Groups}
|
||||
linkTo={"/app/designer/groups"}
|
||||
title={t("Groups")} />
|
||||
{DevSettings.futureFeaturesEnabled() &&
|
||||
<NavTab
|
||||
panel={Panel.SavedGardens}
|
||||
linkTo={"/app/designer/gardens"}
|
||||
title={t("Gardens")} />}
|
||||
<NavTab
|
||||
panel={Panel.SavedGardens}
|
||||
linkTo={"/app/designer/gardens"}
|
||||
title={t("Gardens")} />
|
||||
<NavTab
|
||||
panel={Panel.FarmEvents}
|
||||
linkTo={"/app/designer/events"}
|
||||
|
|
|
@ -3,6 +3,8 @@ import { mount } from "enzyme";
|
|||
import { RawAddGarden as AddGarden, mapStateToProps } from "../garden_add";
|
||||
import { GardenSnapshotProps } from "../garden_snapshot";
|
||||
import { fakeState } from "../../../__test_support__/fake_state";
|
||||
import { buildResourceIndex } from "../../../__test_support__/resource_index_builder";
|
||||
import { fakeSavedGarden } from "../../../__test_support__/fake_state/resources";
|
||||
|
||||
describe("<AddGarden />", () => {
|
||||
const fakeProps = (): GardenSnapshotProps => ({
|
||||
|
@ -22,4 +24,13 @@ describe("mapStateToProps()", () => {
|
|||
const props = mapStateToProps(fakeState());
|
||||
expect(props.currentSavedGarden).toEqual(undefined);
|
||||
});
|
||||
|
||||
it("finds saved garden", () => {
|
||||
const state = fakeState();
|
||||
const savedGarden = fakeSavedGarden();
|
||||
state.resources = buildResourceIndex([savedGarden]);
|
||||
state.resources.consumers.farm_designer.openedSavedGarden = savedGarden.uuid;
|
||||
const props = mapStateToProps(state);
|
||||
expect(props.currentSavedGarden).toEqual(savedGarden);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -86,6 +86,15 @@ describe("<EditGarden />", () => {
|
|||
const wrapper = mount(<EditGarden {...p} />);
|
||||
expect(wrapper.text()).toContain("exit");
|
||||
});
|
||||
|
||||
it("renders with missing data", () => {
|
||||
const p = fakeProps();
|
||||
p.savedGarden = fakeSavedGarden();
|
||||
p.savedGarden.body.id = undefined;
|
||||
p.savedGarden.body.name = undefined;
|
||||
const wrapper = mount(<EditGarden {...p} />);
|
||||
expect(wrapper.text().toLowerCase()).toContain("edit garden");
|
||||
});
|
||||
});
|
||||
|
||||
describe("mapStateToProps()", () => {
|
||||
|
@ -100,4 +109,16 @@ describe("mapStateToProps()", () => {
|
|||
expect(props.gardenIsOpen).toEqual(true);
|
||||
expect(props.savedGarden).toEqual(sg);
|
||||
});
|
||||
|
||||
it("doesn't find saved garden", () => {
|
||||
const sg = fakeSavedGarden();
|
||||
sg.body.id = 1;
|
||||
mockPath = "/app/designer/gardens/";
|
||||
const state = fakeState();
|
||||
state.resources = buildResourceIndex([sg]);
|
||||
state.resources.consumers.farm_designer.openedSavedGarden = sg.uuid;
|
||||
const props = mapStateToProps(state);
|
||||
expect(props.gardenIsOpen).toEqual(false);
|
||||
expect(props.savedGarden).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -95,7 +95,11 @@ export const copySavedGarden = ({ newSGName, savedGarden, plantTemplates }: {
|
|||
const sourceSavedGardenId = savedGarden.body.id;
|
||||
const name = newSGName || `${savedGarden.body.name} (${t("copy")})`;
|
||||
dispatch(initSaveGetId(savedGarden.kind, { name }))
|
||||
.then((newSGId: number) => plantTemplates
|
||||
.filter(x => x.body.saved_garden_id === sourceSavedGardenId)
|
||||
.map(x => dispatch(initSave(x.kind, newPTBody(x, newSGId)))));
|
||||
.then((newSGId: number) => {
|
||||
plantTemplates
|
||||
.filter(x => x.body.saved_garden_id === sourceSavedGardenId)
|
||||
.map(x => dispatch(initSave(x.kind, newPTBody(x, newSGId))));
|
||||
success(t("Garden Saved."));
|
||||
history.push("/app/designer/gardens");
|
||||
});
|
||||
};
|
||||
|
|
|
@ -2,8 +2,9 @@ import * as React from "react";
|
|||
import { connect } from "react-redux";
|
||||
import { Everything } from "../../interfaces";
|
||||
import { GardenSnapshotProps, GardenSnapshot } from "./garden_snapshot";
|
||||
import { findSavedGarden } from "./garden_edit";
|
||||
import { selectAllPlantTemplates } from "../../resources/selectors";
|
||||
import {
|
||||
selectAllPlantTemplates, findSavedGarden
|
||||
} from "../../resources/selectors";
|
||||
import {
|
||||
DesignerPanel, DesignerPanelHeader, DesignerPanelContent
|
||||
} from "../plants/designer_panel";
|
||||
|
@ -12,11 +13,15 @@ import { Row } from "../../ui";
|
|||
import { t } from "../../i18next_wrapper";
|
||||
import { Panel } from "../panel_header";
|
||||
|
||||
export const mapStateToProps = (props: Everything): GardenSnapshotProps => ({
|
||||
currentSavedGarden: findSavedGarden(props.resources.index),
|
||||
dispatch: props.dispatch,
|
||||
plantTemplates: selectAllPlantTemplates(props.resources.index),
|
||||
});
|
||||
export const mapStateToProps = (props: Everything): GardenSnapshotProps => {
|
||||
const { openedSavedGarden } = props.resources.consumers.farm_designer;
|
||||
return {
|
||||
currentSavedGarden: openedSavedGarden
|
||||
? findSavedGarden(props.resources.index, openedSavedGarden) : undefined,
|
||||
dispatch: props.dispatch,
|
||||
plantTemplates: selectAllPlantTemplates(props.resources.index),
|
||||
};
|
||||
};
|
||||
|
||||
export class RawAddGarden extends React.Component<GardenSnapshotProps, {}> {
|
||||
render() {
|
||||
|
|
|
@ -7,7 +7,7 @@ import { BlurableInput, Row } from "../../ui";
|
|||
import { edit, save } from "../../api/crud";
|
||||
import { connect } from "react-redux";
|
||||
import {
|
||||
selectAllSavedGardens, selectAllPlantPointers, findResourceById
|
||||
selectAllPlantPointers, maybeFindSavedGardenById
|
||||
} from "../../resources/selectors";
|
||||
import { Everything } from "../../interfaces";
|
||||
import {
|
||||
|
@ -53,18 +53,17 @@ const DestroyGardenButton =
|
|||
{t("delete")}
|
||||
</button>;
|
||||
|
||||
export const findSavedGarden = (ri: ResourceIndex) => {
|
||||
export const findSavedGardenByUrl = (ri: ResourceIndex) => {
|
||||
const id = getPathArray()[4];
|
||||
const num = parseInt(id || "NOPE", 10);
|
||||
if (isNumber(num) && !isNaN(num)) {
|
||||
const uuid = findResourceById(ri, "SavedGarden", num);
|
||||
return selectAllSavedGardens(ri).filter(x => x.uuid === uuid)[0];
|
||||
return maybeFindSavedGardenById(ri, num);
|
||||
}
|
||||
};
|
||||
|
||||
export const mapStateToProps = (props: Everything): EditGardenProps => {
|
||||
const { openedSavedGarden } = props.resources.consumers.farm_designer;
|
||||
const savedGarden = findSavedGarden(props.resources.index);
|
||||
const savedGarden = findSavedGardenByUrl(props.resources.index);
|
||||
return {
|
||||
savedGarden,
|
||||
gardenIsOpen: !!(savedGarden && savedGarden.uuid === openedSavedGarden),
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
sanityCheck,
|
||||
isTaggedPlantTemplate,
|
||||
isTaggedGenericPointer,
|
||||
isTaggedSavedGarden,
|
||||
} from "./tagged_resources";
|
||||
import {
|
||||
ResourceName,
|
||||
|
@ -113,6 +114,13 @@ export function maybeFindPointById(index: ResourceIndex, id: number) {
|
|||
if (resource && isTaggedGenericPointer(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)];
|
||||
const resource = index.references[uuid || "nope"];
|
||||
if (resource && isTaggedSavedGarden(resource)) { return resource; }
|
||||
}
|
||||
|
||||
export let findRegimenById = (ri: ResourceIndex, regimen_id: number) => {
|
||||
const regimen = byId("Regimen")(ri, regimen_id);
|
||||
if (regimen && isTaggedRegimen(regimen) && sanityCheck(regimen)) {
|
||||
|
|
|
@ -59,6 +59,7 @@ export let findRegimen = uuidFinder<TaggedRegimen>("Regimen");
|
|||
export let findFarmEvent = uuidFinder<TaggedFarmEvent>("FarmEvent");
|
||||
export let findPoints = uuidFinder<TaggedPoint>("Point");
|
||||
export let findPointGroup = uuidFinder<TaggedPoint>("Point");
|
||||
export let findSavedGarden = uuidFinder<TaggedSavedGarden>("SavedGarden");
|
||||
|
||||
export const selectAllCrops =
|
||||
(i: ResourceIndex) => findAll<TaggedCrop>(i, "Crop");
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
PointerType,
|
||||
SpecialStatus,
|
||||
TaggedPlantTemplate,
|
||||
TaggedSavedGarden,
|
||||
} from "farmbot";
|
||||
|
||||
export interface TaggedResourceBase {
|
||||
|
@ -98,5 +99,7 @@ export let isTaggedGenericPointer =
|
|||
(x: object): x is TaggedGenericPointer => {
|
||||
return isTaggedPoint(x) && (x.body.pointer_type === "GenericPointer");
|
||||
};
|
||||
export let isTaggedSavedGarden =
|
||||
(x: object): x is TaggedSavedGarden => is("SavedGarden")(x);
|
||||
export let isTaggedPlantTemplate =
|
||||
(x: object): x is TaggedPlantTemplate => is("PlantTemplate")(x);
|
||||
|
|
|
@ -62,7 +62,7 @@ describe Api::SavedGardensController do
|
|||
gardens_b4 = user.device.saved_gardens.count
|
||||
templates_b4 = user.device.plant_templates.count
|
||||
plants = FactoryBot.create_list(:plant, 3, device: user.device)
|
||||
post :snapshot
|
||||
post :snapshot, body: {}.to_json
|
||||
expect(response.status).to eq(200)
|
||||
expect(user.device.plant_templates.count).to eq(plants.length)
|
||||
expect(user.device.saved_gardens.count).to be > gardens_b4
|
||||
|
|
Loading…
Reference in New Issue