import { TaggedPointGroup } from "farmbot"; import { cloneDeep, isNumber, isUndefined } from "lodash"; import { SelectionBoxData } from "../../map/background"; import { PointGroupCriteria, POINTER_TYPES, EqCriteria, PointerType, StrAndNumCriteriaKeys, } from "./interfaces"; import { error } from "../../../toast/toast"; import { t } from "../../../i18next_wrapper"; import { overwriteGroup } from "../actions"; /** Update and save group criteria. */ export const editCriteria = (group: TaggedPointGroup, update: Partial) => (dispatch: Function) => { const criteria = { string_eq: update.string_eq || group.body.criteria.string_eq, day: update.day || group.body.criteria.day, number_eq: update.number_eq || group.body.criteria.number_eq, number_gt: update.number_gt || group.body.criteria.number_gt, number_lt: update.number_lt || group.body.criteria.number_lt, }; dispatch(overwriteGroup(group, { ...group.body, criteria })); }; /** Toggle string or number equal criteria. */ export const toggleEqCriteria = ( eqCriteria: EqCriteria, direction?: "on" | "off", ) => (key: string, value: T) => { const values: T[] = eqCriteria[key] || []; if (values.includes(value)) { if (direction != "on") { const newValues = values.filter(s => s != value); eqCriteria[key] = newValues; !newValues.length && delete eqCriteria[key]; } } else { if (direction != "off") { values.push(value); eqCriteria[key] = values; } } }; /** * Toggle and save string or number equal criteria. * When adding criteria with a pointerType provided, clear incompatible criteria. */ export const toggleAndEditEqCriteria = ( group: TaggedPointGroup, key: string, value: T, pointerType?: PointerType, ) => (dispatch: Function) => { const tempCriteria = cloneDeep(group.body.criteria); const criteriaField = typeof value == "string" ? "string_eq" : "number_eq"; const tempEqCriteria = tempCriteria[criteriaField] as EqCriteria; const wasOff = !tempEqCriteria[key]?.includes(value); toggleEqCriteria(tempEqCriteria)(key, value); pointerType && wasOff && clearSubCriteria( POINTER_TYPES.filter(x => x != pointerType), tempCriteria, key); dispatch(editCriteria(group, tempCriteria)); }; /** Clear incompatible criteria. */ export const clearSubCriteria = ( pointerTypes: PointerType[], tempCriteria: PointGroupCriteria, keepKey: string, ) => { const toggleStrEq = toggleEqCriteria(tempCriteria.string_eq, "off"); const toggleNumEq = toggleEqCriteria(tempCriteria.number_eq, "off"); const toggleStrEqMapper = (key: string) => key != keepKey && tempCriteria.string_eq[key]?.map(value => toggleStrEq(key, value)); if (pointerTypes.includes("Plant")) { ["openfarm_slug", "plant_stage"].map(toggleStrEqMapper); } if (pointerTypes.includes("Weed")) { ["meta.created_by", "plant_stage"].map(toggleStrEqMapper); } if (pointerTypes.includes("GenericPointer") && pointerTypes.includes("Weed")) { ["meta.color"].map(toggleStrEqMapper); delete tempCriteria.number_lt.radius; delete tempCriteria.number_gt.radius; } if (pointerTypes.includes("ToolSlot")) { tempCriteria.number_eq.pullout_direction?.map(value => toggleNumEq("pullout_direction", value)); } }; /** * Toggle and save pointer_type string equal criteria. * When removing pointer_type criteria, clear pointer_type-specific criteria. */ export const togglePointTypeCriteria = (group: TaggedPointGroup, pointerType: PointerType, clear = false) => (dispatch: Function) => { const tempCriteria = cloneDeep(group.body.criteria); const toggle = toggleEqCriteria(tempCriteria.string_eq); clear && (tempCriteria.string_eq.pointer_type = []); toggle("pointer_type", pointerType); clearSubCriteria(POINTER_TYPES.filter(x => x != pointerType), tempCriteria, "pointer_type"); dispatch(editCriteria(group, tempCriteria)); }; /** Clear and save all fields in the provided criteria categories. */ export const clearCriteriaField = ( group: TaggedPointGroup, categories: StrAndNumCriteriaKeys, fields: string[], ) => (dispatch: Function) => { const tempCriteria = cloneDeep(group.body.criteria); categories.map(category => fields.map(field => delete tempCriteria[category][field])); dispatch(editCriteria(group, tempCriteria)); }; /** For map selection box actions maybeUpdateGroup. */ export const editGtLtCriteria = (group: TaggedPointGroup, box: SelectionBoxData) => (dispatch: Function) => { if (!(isNumber(box.x0) && isNumber(box.y0) && isNumber(box.x1) && isNumber(box.y1))) { return; } const tempGtCriteria = cloneDeep(group.body.criteria.number_gt); const tempLtCriteria = cloneDeep(group.body.criteria.number_lt); tempGtCriteria.x = Math.min(box.x0, box.x1); tempGtCriteria.y = Math.min(box.y0, box.y1); tempLtCriteria.x = Math.max(box.x0, box.x1); tempLtCriteria.y = Math.max(box.y0, box.y1); dispatch(editCriteria(group, { number_gt: tempGtCriteria, number_lt: tempLtCriteria, })); }; /** For EqCriteriaSelection form. */ export const removeEqCriteriaValue = ( group: TaggedPointGroup, eqCriteria: EqCriteria, eqCriteriaName: string, key: string, value: T, ) => (dispatch: Function) => { const tempCriteriaField = cloneDeep(eqCriteria); toggleEqCriteria(tempCriteriaField, "off")(key, value); dispatch(editCriteria(group, { [eqCriteriaName]: tempCriteriaField })); }; /** * For criteria form NumberLtGtInput. * Clear incompatible criteria if pointer_type is provided. */ export const editGtLtCriteriaField = ( group: TaggedPointGroup, criteriaField: "number_gt" | "number_lt", criteriaKey: string, pointerType?: PointerType, ) => (e: React.FormEvent) => (dispatch: Function) => { const tempCriteria = cloneDeep(group.body.criteria); pointerType && clearSubCriteria( POINTER_TYPES.filter(x => x != pointerType), tempCriteria, criteriaKey); const value = e.currentTarget.value != "" ? parseInt(e.currentTarget.value) : undefined; if (!isUndefined(value)) { const ltValue = tempCriteria.number_lt[criteriaKey]; const gtValue = tempCriteria.number_gt[criteriaKey]; if (criteriaField == "number_lt" && !isUndefined(gtValue) && !(value > gtValue)) { return error(t("Value must be greater than {{ num }}.", { num: gtValue })); } if (criteriaField == "number_gt" && !isUndefined(ltValue) && !(value < ltValue)) { return error(t("Value must be less than {{ num }}.", { num: ltValue })); } } tempCriteria[criteriaField][criteriaKey] = value; dispatch(editCriteria(group, tempCriteria)); };