group fixes and additions
parent
0e85a10dc5
commit
8d3c537332
|
@ -562,6 +562,12 @@ export namespace Content {
|
||||||
export const CONFIRM_PLANT_DELETION =
|
export const CONFIRM_PLANT_DELETION =
|
||||||
trim(`Show a confirmation dialog when deleting a plant.`);
|
trim(`Show a confirmation dialog when deleting a plant.`);
|
||||||
|
|
||||||
|
export const SORT_DESCRIPTION =
|
||||||
|
trim(`When executing a sequence over a Group of locations, FarmBot will
|
||||||
|
travel to each group member in the order of the chosen sort method.
|
||||||
|
If the random option is chosen, FarmBot will travel in a random order
|
||||||
|
every time, so the ordering shown below will only be representative.`);
|
||||||
|
|
||||||
// Device
|
// Device
|
||||||
export const NOT_HTTPS =
|
export const NOT_HTTPS =
|
||||||
trim(`WARNING: Sending passwords via HTTP:// is not secure.`);
|
trim(`WARNING: Sending passwords via HTTP:// is not secure.`);
|
||||||
|
|
|
@ -27,6 +27,7 @@ import {
|
||||||
dropPlant, dragPlant, beginPlantDrag, maybeSavePlantLocation
|
dropPlant, dragPlant, beginPlantDrag, maybeSavePlantLocation
|
||||||
} from "./layers/plants/plant_actions";
|
} from "./layers/plants/plant_actions";
|
||||||
import { chooseLocation } from "../move_to";
|
import { chooseLocation } from "../move_to";
|
||||||
|
import { GroupOrder } from "../point_groups/group_order_visual";
|
||||||
|
|
||||||
export class GardenMap extends
|
export class GardenMap extends
|
||||||
React.Component<GardenMapProps, Partial<GardenMapState>> {
|
React.Component<GardenMapProps, Partial<GardenMapState>> {
|
||||||
|
@ -343,6 +344,9 @@ export class GardenMap extends
|
||||||
data={this.props.designer.currentPoint}
|
data={this.props.designer.currentPoint}
|
||||||
key={"currentPoint"}
|
key={"currentPoint"}
|
||||||
mapTransformProps={this.mapTransformProps} />
|
mapTransformProps={this.mapTransformProps} />
|
||||||
|
GroupOrder = () => <GroupOrder
|
||||||
|
plants={this.props.plants}
|
||||||
|
mapTransformProps={this.mapTransformProps} />
|
||||||
Bugs = () => showBugs() ? <Bugs mapTransformProps={this.mapTransformProps}
|
Bugs = () => showBugs() ? <Bugs mapTransformProps={this.mapTransformProps}
|
||||||
botSize={this.props.botSize} /> : <g />
|
botSize={this.props.botSize} /> : <g />
|
||||||
|
|
||||||
|
@ -365,6 +369,7 @@ export class GardenMap extends
|
||||||
<this.SelectionBox />
|
<this.SelectionBox />
|
||||||
<this.TargetCoordinate />
|
<this.TargetCoordinate />
|
||||||
<this.DrawnPoint />
|
<this.DrawnPoint />
|
||||||
|
<this.GroupOrder />
|
||||||
<this.Bugs />
|
<this.Bugs />
|
||||||
</svg>
|
</svg>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { fakePointGroup } from "../../../__test_support__/fake_state/resources";
|
||||||
|
const mockGroup = fakePointGroup();
|
||||||
|
mockGroup.body.point_ids = [1, 2, 3];
|
||||||
|
jest.mock("../group_detail", () => ({ fetchGroupFromUrl: () => mockGroup }));
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import { mount } from "enzyme";
|
||||||
|
import { GroupOrder, GroupOrderProps } from "../group_order_visual";
|
||||||
|
import {
|
||||||
|
fakeMapTransformProps
|
||||||
|
} from "../../../__test_support__/map_transform_props";
|
||||||
|
import { fakePlant } from "../../../__test_support__/fake_state/resources";
|
||||||
|
|
||||||
|
describe("<GroupOrder />", () => {
|
||||||
|
const fakeProps = (): GroupOrderProps => {
|
||||||
|
const plant1 = fakePlant();
|
||||||
|
plant1.body.id = 1;
|
||||||
|
const plant2 = fakePlant();
|
||||||
|
plant2.body.id = 2;
|
||||||
|
const plant3 = fakePlant();
|
||||||
|
plant3.body.id = 3;
|
||||||
|
const plant4 = fakePlant();
|
||||||
|
plant4.body.id = undefined;
|
||||||
|
const plant5 = fakePlant();
|
||||||
|
plant5.body.id = 5;
|
||||||
|
return {
|
||||||
|
mapTransformProps: fakeMapTransformProps(),
|
||||||
|
plants: [plant1, plant2, plant3],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
it("renders group order", () => {
|
||||||
|
const wrapper = mount(<GroupOrder {...fakeProps()} />);
|
||||||
|
expect(wrapper.find("line").length).toEqual(3);
|
||||||
|
});
|
||||||
|
});
|
|
@ -4,8 +4,9 @@ import { init, save } from "../../api/crud";
|
||||||
import { history } from "../../history";
|
import { history } from "../../history";
|
||||||
import { GetState } from "../../redux/interfaces";
|
import { GetState } from "../../redux/interfaces";
|
||||||
import { findPointGroup } from "../../resources/selectors";
|
import { findPointGroup } from "../../resources/selectors";
|
||||||
|
import { t } from "../../i18next_wrapper";
|
||||||
|
|
||||||
const UNTITLED = "Untitled Group";
|
const UNTITLED = () => t("Untitled Group");
|
||||||
|
|
||||||
interface CreateGroupProps {
|
interface CreateGroupProps {
|
||||||
/** TaggedPoint UUIDs */
|
/** TaggedPoint UUIDs */
|
||||||
|
@ -21,7 +22,7 @@ export const createGroup = ({ points, name }: CreateGroupProps) => {
|
||||||
.map(x => x ? x.body.id : undefined);
|
.map(x => x ? x.body.id : undefined);
|
||||||
const point_ids = betterCompact(possiblyNil);
|
const point_ids = betterCompact(possiblyNil);
|
||||||
const group: PointGroup =
|
const group: PointGroup =
|
||||||
({ name: name || UNTITLED, point_ids, sort_type: "xy_ascending" });
|
({ name: name || UNTITLED(), point_ids, sort_type: "xy_ascending" });
|
||||||
const action = init("PointGroup", group);
|
const action = init("PointGroup", group);
|
||||||
dispatch(action);
|
dispatch(action);
|
||||||
return dispatch(save(action.payload.uuid)).then(() => {
|
return dispatch(save(action.payload.uuid)).then(() => {
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { store } from "../../redux/store";
|
||||||
|
import { MapTransformProps, TaggedPlant } from "../map/interfaces";
|
||||||
|
import { fetchGroupFromUrl } from "./group_detail";
|
||||||
|
import { isUndefined } from "lodash";
|
||||||
|
import { sortGroupBy } from "./point_group_sort_selector";
|
||||||
|
import { Color } from "../../ui";
|
||||||
|
import { transformXY } from "../map/util";
|
||||||
|
|
||||||
|
export interface GroupOrderProps {
|
||||||
|
plants: TaggedPlant[];
|
||||||
|
mapTransformProps: MapTransformProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sortedPointCoordinates =
|
||||||
|
(plants: TaggedPlant[]): { x: number, y: number }[] => {
|
||||||
|
const group = fetchGroupFromUrl(store.getState().resources.index);
|
||||||
|
if (isUndefined(group)) { return []; }
|
||||||
|
const groupPlants = plants
|
||||||
|
.filter(p => group.body.point_ids.includes(p.body.id || 0));
|
||||||
|
return sortGroupBy(group.body.sort_type, groupPlants)
|
||||||
|
.map(p => ({ x: p.body.x, y: p.body.y }));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GroupOrder = (props: GroupOrderProps) => {
|
||||||
|
const points = sortedPointCoordinates(props.plants);
|
||||||
|
return <g id="group-order"
|
||||||
|
stroke={Color.mediumGray} strokeWidth={3} strokeDasharray={12}>
|
||||||
|
{points.map((p, i) => {
|
||||||
|
const prev = i > 0 ? points[i - 1] : p;
|
||||||
|
const one = transformXY(prev.x, prev.y, props.mapTransformProps);
|
||||||
|
const two = transformXY(p.x, p.y, props.mapTransformProps);
|
||||||
|
return <line key={i} x1={one.qx} y1={one.qy} x2={two.qx} y2={two.qy} />;
|
||||||
|
})}
|
||||||
|
</g>;
|
||||||
|
};
|
|
@ -1,41 +1,38 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { PointGroupSortType } from "farmbot/dist/resources/api_resources";
|
import { PointGroupSortType } from "farmbot/dist/resources/api_resources";
|
||||||
import {
|
import { FBSelect, DropDownItem } from "../../ui";
|
||||||
FBSelect,
|
|
||||||
DropDownItem
|
|
||||||
} from "../../ui";
|
|
||||||
import { t } from "../../i18next_wrapper";
|
import { t } from "../../i18next_wrapper";
|
||||||
import { trim } from "../../util/util";
|
|
||||||
import { TaggedPlant } from "../map/interfaces";
|
import { TaggedPlant } from "../map/interfaces";
|
||||||
import { shuffle, sortBy } from "lodash";
|
import { shuffle, sortBy } from "lodash";
|
||||||
|
import { Content } from "../../constants";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onChange(value: PointGroupSortType): void;
|
onChange(value: PointGroupSortType): void;
|
||||||
value: PointGroupSortType;
|
value: PointGroupSortType;
|
||||||
}
|
}
|
||||||
|
|
||||||
const optionsTable: Record<PointGroupSortType, string> = {
|
const optionsTable = (): Record<PointGroupSortType, string> => ({
|
||||||
"random": "Random Order",
|
"random": t("Random Order"),
|
||||||
"xy_ascending": "X/Y, Ascending",
|
"xy_ascending": t("X/Y, Ascending"),
|
||||||
"xy_descending": "X/Y, Descending",
|
"xy_descending": t("X/Y, Descending"),
|
||||||
"yx_ascending": "Y/X, Ascending",
|
"yx_ascending": t("Y/X, Ascending"),
|
||||||
"yx_descending": "Y/X Descending",
|
"yx_descending": t("Y/X, Descending"),
|
||||||
}; // Typechecker will remind us when this needs an update. Don't simplify - RC
|
}); // Typechecker will remind us when this needs an update. Don't simplify - RC
|
||||||
|
|
||||||
const optionPlusDescriptions =
|
const optionPlusDescriptions = () =>
|
||||||
(Object
|
(Object
|
||||||
.entries(optionsTable) as [PointGroupSortType, string][])
|
.entries(optionsTable()) as [PointGroupSortType, string][])
|
||||||
.map(x => ({ label: x[1], value: x[0] }));
|
.map(x => ({ label: x[1], value: x[0] }));
|
||||||
|
|
||||||
const optionList =
|
const optionList =
|
||||||
optionPlusDescriptions.map(x => x.value);
|
optionPlusDescriptions().map(x => x.value);
|
||||||
|
|
||||||
export const isSortType = (x: unknown): x is PointGroupSortType => {
|
export const isSortType = (x: unknown): x is PointGroupSortType => {
|
||||||
return optionList.includes(x as PointGroupSortType);
|
return optionList.includes(x as PointGroupSortType);
|
||||||
};
|
};
|
||||||
|
|
||||||
const selected = (value: PointGroupSortType) => ({
|
const selected = (value: PointGroupSortType) => ({
|
||||||
label: t(optionsTable[value] || value),
|
label: t(optionsTable()[value] || value),
|
||||||
value: value
|
value: value
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -44,13 +41,6 @@ export const sortTypeChange = (cb: Function) => (ddi: DropDownItem) => {
|
||||||
isSortType(value) && cb(value);
|
isSortType(value) && cb(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const SORT_DESC = trim(`When executing a sequence
|
|
||||||
over a Group of locations, FarmBot will travel to
|
|
||||||
each group member in the order of the chosen sort
|
|
||||||
method. If the random option is chosen, FarmBot will
|
|
||||||
travel in a random order every time, so the
|
|
||||||
ordering shown below will only be representative.`);
|
|
||||||
|
|
||||||
export function PointGroupSortSelector(p: Props) {
|
export function PointGroupSortSelector(p: Props) {
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
|
@ -60,11 +50,11 @@ export function PointGroupSortSelector(p: Props) {
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<FBSelect
|
<FBSelect
|
||||||
list={optionPlusDescriptions}
|
list={optionPlusDescriptions()}
|
||||||
selectedItem={selected(p.value as PointGroupSortType)}
|
selectedItem={selected(p.value as PointGroupSortType)}
|
||||||
onChange={sortTypeChange(p.onChange)} />
|
onChange={sortTypeChange(p.onChange)} />
|
||||||
<p>
|
<p>
|
||||||
{(p.value == "random") ? t(SORT_DESC) : ""}
|
{(p.value == "random") ? t(Content.SORT_DESCRIPTION) : ""}
|
||||||
</p>
|
</p>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,19 +18,21 @@ npm run translation-check
|
||||||
|
|
||||||
See the [README](https://github.com/FarmBot/Farmbot-Web-App#translating-the-web-app-into-your-language) for contribution instructions.
|
See the [README](https://github.com/FarmBot/Farmbot-Web-App#translating-the-web-app-into-your-language) for contribution instructions.
|
||||||
|
|
||||||
Total number of phrases identified by the language helper for translation: __1086__
|
Total number of phrases identified by the language helper for translation: __1101__
|
||||||
|
|
||||||
|Language|Percent translated|Translated|Untranslated|Other Translations|
|
|Language|Percent translated|Translated|Untranslated|Other Translations|
|
||||||
|:---:|---:|---:|---:|---:|
|
|:---:|---:|---:|---:|---:|
|
||||||
|da|10%|110|976|27|
|
|af|100%|1101|0|1|
|
||||||
|de|39%|421|665|125|
|
|da|10%|110|991|31|
|
||||||
|es|94%|1019|67|156|
|
|de|38%|420|681|128|
|
||||||
|fr|70%|765|321|182|
|
|es|92%|1015|86|160|
|
||||||
|it|8%|89|997|175|
|
|fr|69%|762|339|186|
|
||||||
|nl|7%|79|1007|145|
|
|it|8%|89|1012|178|
|
||||||
|pt|7%|71|1015|164|
|
|nl|7%|79|1022|148|
|
||||||
|ru|56%|606|480|205|
|
|pt|6%|71|1030|167|
|
||||||
|zh|8%|86|1000|145|
|
|ru|55%|604|497|208|
|
||||||
|
|th|0%|0|1101|0|
|
||||||
|
|zh|8%|86|1015|148|
|
||||||
|
|
||||||
**Percent translated** refers to the percent of phrases identified by the
|
**Percent translated** refers to the percent of phrases identified by the
|
||||||
language helper that have been translated. Additional phrases not identified
|
language helper that have been translated. Additional phrases not identified
|
||||||
|
|
Loading…
Reference in New Issue