Merge pull request #626 from gibsonliketheguitar/feat-147

Feat-147 & Feat-166
This commit is contained in:
Shams
2022-02-01 02:13:30 +11:00
committed by GitHub
15 changed files with 598 additions and 267 deletions

View File

@@ -0,0 +1,68 @@
import Subheading from "@src/components/Table/ColumnMenu/Subheading";
import EditIcon from "@mui/icons-material/Edit";
import IconButton from "@mui/material/IconButton";
import Grid from "@mui/material/Grid";
import Divider from "@mui/material/Divider";
import { IConditionModal } from "./Settings";
import { createValueLabel } from "./utils/conditionListHelper";
interface I_ConditionList {
config: Record<string, any>;
setModal: React.Dispatch<React.SetStateAction<IConditionModal>>;
}
export default function ConditionList({ config, setModal }: I_ConditionList) {
const conditions = config?.conditions ?? [];
const noConditions = Boolean(conditions?.length < 1); // Double check this
if (noConditions) {
return (
<>
No conditions set yet
<br />
</>
);
}
return (
<>
<Subheading>Conditions</Subheading>
{conditions.map((condition, index) => {
return (
<>
<Grid
container
justifyContent="space-between"
alignItems={"center"}
>
<GridItem
index={index}
condition={condition}
setModal={setModal}
/>
</Grid>
<Divider />
</>
);
})}
</>
);
}
const GridItem = ({ condition, setModal, index }: any) => {
const noCondition = Boolean(!condition);
if (noCondition) return <></>;
return (
<>
{condition?.label}
<Grid item>
{createValueLabel(condition)}
<IconButton
onClick={() => setModal({ isOpen: true, condition, index })}
>
<EditIcon />
</IconButton>
</Grid>
</>
);
};

View File

@@ -0,0 +1,113 @@
import { useEffect } from "react";
import _find from "lodash/find";
import Modal from "@src/components/Modal";
import DeleteIcon from "@mui/icons-material/Delete";
import { default as Content } from "./ConditionModalContent";
import { EMPTY_STATE } from "./Settings";
import { isElement, isEmpty } from "lodash";
export default function ConditionModal({
modal,
setModal,
conditions,
setConditions,
}) {
const handleClose = () => setModal(EMPTY_STATE);
const handleSave = () => {
let _conditions = [...conditions];
_conditions[modal.index] = modal.condition;
setConditions(_conditions);
setModal(EMPTY_STATE);
};
const handleAdd = () => {
const labelIsEmpty = Boolean(modal.condition.label.length < 4);
const stringValueIsEmpty = Boolean(
modal.condition.type === "string" && modal.condition.value.length === 0
);
const hasDuplicate = Boolean(_find(conditions, modal.condition));
const validation = Boolean(
labelIsEmpty || stringValueIsEmpty || hasDuplicate
);
if (validation) return;
function setConditionHack(type, condition) {
let rCondition = condition;
if (type === "undefined") rCondition = { ...condition, value: undefined };
if (type === "boolean" && typeof condition.value === "object")
rCondition = { ...condition, value: false }; //Again 'rowy's multiselect does not accept default value'
return rCondition;
}
const modalCondition = setConditionHack(
modal.condition.type,
modal.condition
);
const noConditions = Boolean(conditions?.length === 0 || !conditions);
const arr = noConditions
? [modalCondition]
: [...conditions, modalCondition];
setConditions(arr);
setModal(EMPTY_STATE);
};
const handleRemove = () => {
const _newConditions = conditions.filter(
(c, index) => index !== modal.index
);
setConditions(_newConditions);
setModal(EMPTY_STATE);
};
const handleUpdate = (key: string) => (value) => {
const newState = {
...modal,
condition: { ...modal.condition, [key]: value },
};
setModal(newState);
};
const primaryAction = (index) => {
return index === null
? {
children: "Add condition",
onClick: () => handleAdd(),
disabled: false,
}
: {
children: "Save changes",
onClick: () => handleSave(),
disabled: false,
};
};
const secondaryAction = (index) => {
return index === null
? {
children: "Cancel",
onClick: () => setModal(EMPTY_STATE),
}
: {
startIcon: <DeleteIcon />,
children: "Remove condition",
onClick: () => handleRemove(),
};
};
useEffect(() => {
handleUpdate("operator")(modal.condition.operator ?? "==");
}, [modal.condition.type]);
return (
<Modal
open={modal.isOpen}
title={`${modal.index ? "Edit" : "Add"} condition`}
maxWidth={"xs"}
onClose={handleClose}
actions={{
primary: primaryAction(modal.index),
secondary: secondaryAction(modal.index),
}}
children={
<Content
condition={modal.condition}
conditions={conditions}
handleUpdate={handleUpdate}
/>
}
/>
);
}

View File

@@ -0,0 +1,105 @@
import _find from "lodash/find";
import Grid from "@mui/material/Grid";
import TextField from "@mui/material/TextField";
import Typography from "@mui/material/Typography";
import MultiSelect from "@rowy/multiselect";
interface I_ConditionModalContent {
handleUpdate: () => void;
modal: any;
}
const multiSelectOption = [
{ label: "Boolean", value: "boolean" },
{ label: "Number", value: "number" },
{ label: "String", value: "string" },
{ label: "Undefined", value: "undefined" },
{ label: "Null", value: "null" },
];
const booleanOptions = [
{ label: "True", value: "true" },
{ label: "False", value: "false" },
];
const operatorOptions = [
{ label: "Less than", value: "<" },
{ label: "Less than or equal", value: "<=" },
{ label: "Equal", value: "==" },
{ label: "Equal or more than", value: ">=" },
{ label: "More than", value: ">" },
];
export default function ConditionModalContent({
condition,
conditions,
handleUpdate,
}: any) {
const { label, operator, type, value } = condition;
const duplicateCond = Boolean(_find(conditions, condition));
const labelReqLen = Boolean(condition.label.length < 4);
return (
<>
<Typography variant="overline">DATA TYPE (input)</Typography>
<MultiSelect
options={multiSelectOption}
onChange={(v) => handleUpdate("type")(v)}
value={type}
multiple={false}
label="Select data type"
/>
{/** This is the issue where false is causing a problem */}
{/** To add defaultValue into MultiSelect?*/}
{type === "boolean" && (
<MultiSelect
options={booleanOptions}
onChange={(v) => handleUpdate("value")(v === "true")}
value={value ? "true" : "false"}
multiple={false}
label="Select condition value"
/>
)}
{type === "number" && (
<Grid container direction="row" justifyContent="space-between">
<div style={{ width: "45%" }}>
{console.log(operatorOptions)}
<MultiSelect
options={operatorOptions}
onChange={(v) => handleUpdate("operator")(v)}
value={operator}
multiple={false}
label="Select operator"
/>
</div>
<TextField
error={duplicateCond}
type="number"
label="Value"
value={value}
onChange={(e) => handleUpdate("value")(Number(e.target.value))}
helperText={
duplicateCond ? "Numeric Conditional already exists" : ""
}
/>
</Grid>
)}
{type === "string" && (
<TextField
error={duplicateCond}
fullWidth
label="Value"
value={value}
onChange={(e) => handleUpdate("value")(e.target.value)}
helperText={duplicateCond ? "string value already exists" : ""}
/>
)}
<TextField
error={labelReqLen}
value={label}
label="Label"
fullWidth
onChange={(e) => handleUpdate("label")(e.target.value)}
/>
</>
);
}

View File

@@ -0,0 +1,12 @@
import { IFilterOperator } from "../types";
export const filterOperators: IFilterOperator[] = [
{
label: "equals",
value: "==",
},
{
label: "not equals",
value: "!=",
},
];

View File

@@ -0,0 +1,71 @@
import { forwardRef, useMemo } from "react";
import { IPopoverInlineCellProps } from "../types";
import { ButtonBase } from "@mui/material";
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
import _find from "lodash/find";
import getLabel from "./utils/getLabelHelper";
import { LowPriority } from "@mui/icons-material";
export const StatusSingleSelect = forwardRef(function StatusSingleSelect(
{ column, value, showPopoverCell, disabled }: IPopoverInlineCellProps,
ref: React.Ref<any>
) {
const conditions = column.config?.conditions ?? [];
const lowPriorityOperator = ["<", "<=", ">=", ">"];
const otherOperator = conditions.filter(
(c) => !lowPriorityOperator.includes(c.operator)
);
/**Revisit this */
const sortLowPriorityList = conditions
.filter((c) => {
return lowPriorityOperator.includes(c.operator);
})
.sort((a, b) => {
const aDistFromValue = Math.abs(value - a.value);
const bDistFromValue = Math.abs(value - b.value);
//return the smallest distance
return aDistFromValue - bDistFromValue;
});
const sortedConditions = [...otherOperator, ...sortLowPriorityList];
const label = useMemo(
() => getLabel(value, sortedConditions),
[value, sortedConditions]
);
return (
<ButtonBase
onClick={() => showPopoverCell(true)}
ref={ref}
disabled={disabled}
className="cell-collapse-padding"
style={{
padding: "var(--cell-padding)",
paddingRight: 0,
height: "100%",
font: "inherit",
color: "inherit !important",
letterSpacing: "inherit",
textAlign: "inherit",
justifyContent: "flex-start",
}}
>
<div style={{ flexGrow: 1, overflow: "hidden" }}>{label}</div>
{!disabled && (
<ArrowDropDownIcon
className="row-hover-iconButton"
sx={{
flexShrink: 0,
mr: 0.5,
borderRadius: 1,
p: (32 - 24) / 2 / 8,
boxSizing: "content-box",
}}
/>
)}
</ButtonBase>
);
});
export default StatusSingleSelect;

View File

@@ -0,0 +1,48 @@
import _find from "lodash/find";
import { IPopoverCellProps } from "../types";
import MultiSelect_ from "@rowy/multiselect";
export default function StatusSingleSelect({
value,
onSubmit,
column,
parentRef,
showPopoverCell,
disabled,
}: IPopoverCellProps) {
const config = column.config ?? {};
const conditions = config.conditions ?? [];
/**Revisit eventually, can we abstract or use a helper function to clean this? */
const reMappedConditions = conditions.map((c) => {
let rValue = { ...c };
if (c.type === "number") {
if (c.operator === "<") rValue = { ...c, value: c.value - 1 };
if (c.operator === ">") rValue = { ...c, value: c.value + 1 };
}
return rValue;
});
return (
<MultiSelect_
value={value}
onChange={(v) => onSubmit(v)}
options={conditions.length >= 1 ? reMappedConditions : []} // this handles when conditions are deleted
multiple={false}
freeText={config.freeText}
disabled={disabled}
label={column.name as string}
labelPlural={column.name as string}
TextFieldProps={{
style: { display: "none" },
SelectProps: {
open: true,
MenuProps: {
anchorEl: parentRef,
anchorOrigin: { vertical: "bottom", horizontal: "left" },
transformOrigin: { vertical: "top", horizontal: "left" },
},
},
}}
onClose={() => showPopoverCell(false)}
/>
);
}

View File

@@ -1,21 +1,12 @@
import { useState, useEffect } from "react";
import { useState } from "react";
import { ISettingsProps } from "../types";
import Subheading from "@src/components/Table/ColumnMenu/Subheading";
import Button from "@mui/material/Button";
import IconButton from "@mui/material/IconButton";
import Grid from "@mui/material/Grid";
import Divider from "@mui/material/Divider";
import EditIcon from "@mui/icons-material/Edit";
import AddIcon from "@mui/icons-material/Add";
import Modal from "@src/components/Modal";
import DeleteIcon from "@mui/icons-material/Delete";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import MultiSelect from "@rowy/multiselect";
import Button from "@mui/material/Button";
import ConditionModal from "./ConditionModal";
import ConditionList from "./ConditionList";
const EMPTY_STATE: {
export interface IConditionModal {
isOpen: boolean;
index: number | null;
condition: {
@@ -24,7 +15,9 @@ const EMPTY_STATE: {
label: string;
operator: string | undefined;
};
} = {
}
export const EMPTY_STATE: IConditionModal = {
index: null,
isOpen: false,
condition: {
@@ -34,190 +27,12 @@ const EMPTY_STATE: {
operator: "==",
},
};
const ConditionModal = ({ modal, setModal, conditions, setConditions }) => {
const handleClose = () => {
setModal(EMPTY_STATE);
};
const handleSave = () => {
let _conditions = [...conditions];
_conditions[modal.index] = modal.condition;
setConditions(_conditions);
setModal(EMPTY_STATE);
};
const handleAdd = () => {
setConditions(
conditions ? [...conditions, modal.condition] : [modal.condition]
);
setModal(EMPTY_STATE);
};
const handleRemove = () => {
let _conditions = [...conditions];
delete _conditions[modal.index];
setConditions(_conditions);
setModal(EMPTY_STATE);
};
const handleUpdate = (key: string) => (value) => {
setModal({ ...modal, condition: { ...modal.condition, [key]: value } });
};
useEffect(() => {
handleUpdate("operator")(modal.condition.operator ?? "==");
}, [modal.condition.type]);
return (
<Modal
open={modal.isOpen}
title={`${modal.index ? "Edit" : "Add"} condition`}
maxWidth={"xs"}
onClose={handleClose}
actions={{
primary:
modal.index === null
? {
children: "Add condition",
onClick: handleAdd,
disabled: false,
}
: {
children: "Save changes",
onClick: handleSave,
disabled: false,
},
secondary:
modal.index === null
? {
children: "Cancel",
onClick: () => {
setModal(EMPTY_STATE);
},
}
: {
startIcon: <DeleteIcon />,
children: "Remove condition",
onClick: handleRemove,
},
}}
children={
<>
<Typography variant="overline">DATA TYPE (input)</Typography>
<MultiSelect
options={[
{ label: "Boolean", value: "boolean" },
{ label: "Number", value: "number" },
{ label: "String", value: "string" },
{ label: "Undefined", value: "undefined" },
{ label: "Null", value: "null" },
]}
onChange={handleUpdate("type")}
value={modal.condition.type}
multiple={false}
label="Select data type"
/>
<Typography variant="overline">Condition </Typography>
{modal.condition.type === "boolean" && (
<MultiSelect
options={[
{ label: "True", value: "true" },
{ label: "False", value: "false" },
]}
onChange={(v) => handleUpdate("value")(v === "true")}
value={modal.condition.value ? "true" : "false"}
multiple={false}
label="Select condition value"
/>
)}
{modal.condition.type === "number" && (
<Grid container direction="row" justifyContent="space-between">
<div style={{ width: "45%" }}>
<MultiSelect
options={[
{ label: "Less than", value: "<" },
{ label: "Less than or equal", value: "<=" },
{ label: "Equal", value: "==" },
{ label: "Equal or more than", value: ">=" },
{ label: "More than", value: ">" },
]}
onChange={handleUpdate("operator")}
value={modal.condition.operator}
multiple={false}
label="Select operator"
/>
</div>
<TextField
type="number"
label="Value"
value={modal.condition.value}
onChange={(e) => handleUpdate("value")(e.target.value)}
/>
</Grid>
)}
{modal.condition.type === "string" && (
<TextField
fullWidth
label="Value"
value={modal.condition.value}
onChange={(e) => handleUpdate("value")(e.target.value)}
/>
)}
<Typography variant="overline">Assigned label (output)</Typography>
<TextField
value={modal.condition.label}
label="Type the cell output"
fullWidth
onChange={(e) => handleUpdate("label")(e.target.value)}
/>
</>
}
/>
);
};
export default function Settings({ onChange, config }: ISettingsProps) {
const [modal, setModal] = useState(EMPTY_STATE);
const { conditions } = config;
return (
<>
<Subheading>Conditions</Subheading>
{conditions ? (
conditions.map((condition, index) => {
return (
<>
<Grid
container
justifyContent="space-between"
alignItems={"center"}
>
{condition.label}
<Grid item>
{["undefined", "null"].includes(condition.type)
? condition.type
: `${condition.type}:${
condition.type === "number" ? condition.operator : ""
}${
condition.type === "boolean"
? JSON.stringify(condition.value)
: condition.value
}`}
<IconButton
onClick={() => {
setModal({ isOpen: true, condition, index });
}}
>
<EditIcon />
</IconButton>
</Grid>
</Grid>
<Divider />
</>
);
})
) : (
<>
No conditions set yet
<br />
</>
)}
<ConditionList config={config} setModal={setModal} />
<Button
onClick={() => setModal({ ...EMPTY_STATE, isOpen: true })}
startIcon={<AddIcon />}

View File

@@ -1,25 +1,36 @@
import { Controller } from "react-hook-form";
import { ISideDrawerFieldProps } from "../types";
import { Grid } from "@mui/material";
import "@mui/lab";
import { useFieldStyles } from "@src/components/SideDrawer/Form/utils";
import { useStatusStyles } from "./styles";
export default function Rating({ control, column }: ISideDrawerFieldProps) {
const fieldClasses = useFieldStyles();
const ratingClasses = useStatusStyles();
import MultiSelect from "@rowy/multiselect";
import getLabel from "./utils/getLabelHelper";
export default function Status({
control,
column,
disabled,
}: ISideDrawerFieldProps) {
const config = column.config ?? {};
return (
<Controller
control={control}
name={column.key}
render={({ field: { value } }) => (
<Grid container alignItems="center" className={fieldClasses.root}>
<>{value}</>
</Grid>
render={({ field: { onChange, onBlur, value } }) => (
<>
<MultiSelect
value={getLabel(value, config?.conditions)}
onChange={onChange}
options={config?.conditions ?? []}
multiple={false}
freeText={config?.freeText}
disabled={disabled}
TextFieldProps={{
label: "",
hiddenLabel: true,
onBlur,
id: `sidedrawer-field-${column.key}`,
}}
/>
</>
)}
/>
);

View File

@@ -1,48 +0,0 @@
import { useMemo } from "react";
import { IHeavyCellProps } from "../types";
import { useStatusStyles } from "./styles";
import _find from "lodash/find";
export default function Status({ column, value }: IHeavyCellProps) {
const statusClasses = useStatusStyles();
const conditions = column.config?.conditions ?? [];
const label = useMemo(() => {
if (["null", "undefined"].includes(typeof value)) {
const condition = _find(conditions, (c) => c.type === typeof value);
return condition?.label;
} else if (typeof value === "number") {
const numberConditions = conditions.filter((c) => c.type === "number");
for (let i = 0; i < numberConditions.length; i++) {
const condition = numberConditions[i];
switch (condition.operator) {
case "<":
if (value < condition.value) return condition.label;
break;
case "<=":
if (value <= condition.value) return condition.label;
break;
case ">=":
if (value >= condition.value) return condition.label;
break;
case ">":
if (value > condition.value) return condition.label;
break;
case "==":
default:
if (value == condition.value) return condition.label;
break;
}
}
} else {
for (let i = 0; i < conditions.length; i++) {
const condition = conditions[i];
if (value == condition.value) return condition.label;
}
}
return JSON.stringify(value);
}, [value, conditions]);
return <>{label}</>;
}

View File

@@ -1,14 +1,14 @@
import { lazy } from "react";
import { IFieldConfig, FieldType } from "@src/components/fields/types";
import withHeavyCell from "../_withTableCell/withHeavyCell";
import StatusIcon from "@src/assets/icons/Status";
import BasicCell from "../_BasicCell/BasicCellNull";
import NullEditor from "@src/components/Table/editors/NullEditor";
const TableCell = lazy(
() => import("./TableCell" /* webpackChunkName: "TableCell-Status" */)
);
import { filterOperators } from "./Filter";
import BasicCell from "../_BasicCell/BasicCellNull";
import PopoverCell from "./PopoverCell";
import InlineCell from "./InlineCell";
import withPopoverCell from "../_withTableCell/withPopoverCell";
const SideDrawerField = lazy(
() =>
import("./SideDrawerField" /* webpackChunkName: "SideDrawerField-Status" */)
@@ -26,10 +26,16 @@ export const config: IFieldConfig = {
initializable: true,
icon: <StatusIcon />,
description: "Displays field value as custom status text. Read-only. ",
TableCell: withHeavyCell(BasicCell, TableCell),
TableCell: withPopoverCell(BasicCell, InlineCell, PopoverCell, {
anchorOrigin: { horizontal: "left", vertical: "bottom" },
transparent: true,
}),
TableEditor: NullEditor as any,
settings: Settings,
SideDrawerField,
requireConfiguration: true,
filter: {
operators: filterOperators,
},
};
export default config;

View File

@@ -0,0 +1,12 @@
export function createValueLabel(condition) {
const { operator, type, value } = condition || {};
const typeLabelMap = new Map([
["undefined", `${type}`],
["null", `${type}`],
["number", ` ${type}:${operator}${value}`],
["boolean", `${type}:${value}`],
]);
const string = typeLabelMap.get(type);
const validString = Boolean(typeof string === "string");
return validString ? string : JSON.stringify(value);
}

View File

@@ -0,0 +1,89 @@
import _find from "lodash/find";
type value = number | "string" | undefined | null;
interface condition {
type: string;
operator: string;
label: string;
value: value;
}
//TODO ADD TYPES
const getFalseyLabelFrom = (arr: condition[], value: string) => {
const falseyType = (value) =>
typeof value === "object" ? "null" : "undefined";
const conditions = _find(arr, (c) => c.type === falseyType(value));
return conditions?.label;
};
const getBooleanLabelFrom = (arr: condition[], value: string) => {
const boolConditions = arr.filter((c) => c.type === "boolean");
for (let c of boolConditions) {
if (value === c.value) return c.label;
}
};
/**
* @param arr conditional array
* @param value if value is not detected, conditional value becomes the default value
* @returns conditional's label || undefined
*/
const getNumericLabelFrom = (arr: condition[], value: number) => {
const numLabelFind = (v, c) => {
const val = Number(v); // need to handle when value is default seted
const condVal: number = Number(c.value);
const handleLessThan = () => {
if (val === condVal) return true;
if (val < condVal) return true;
else return false;
};
const hanldeGreaterThan = () => {
if (val === condVal) return true;
if (val > condVal) return true;
else return false;
};
const operatorMap = new Map([
["<", handleLessThan()],
[">", hanldeGreaterThan()],
["<=", val <= condVal ? true : false],
[">=", val >= condVal ? true : false],
["==", val === condVal ? true : false],
]);
return operatorMap.get(c.operator) ? c.label : undefined;
};
const numConditions = arr.filter((c) => c?.type === "number");
for (let c of numConditions) {
const label = numLabelFind(value, c);
if (typeof label === "string") return label;
}
};
const getLabelFrom = (arr, value) => {
const invalidVal = Boolean(value);
if (invalidVal) return;
for (let c of arr) {
if (value === c.value) return c.label;
}
};
const finalLabel = (label: string | undefined, value) => {
return typeof label === "string" ? label : value;
};
export default function getLabel(value, conditions) {
let _label: any = undefined;
const isBoolean = Boolean(typeof value === "boolean");
const notBoolean = Boolean(typeof value !== "boolean");
const isNullOrUndefined = Boolean(!value && notBoolean);
const isNumeric = Boolean(typeof value === "number");
if (isNullOrUndefined) _label = getFalseyLabelFrom(conditions, value);
else if (isBoolean) _label = getBooleanLabelFrom(conditions, value);
else if (isNumeric) _label = getNumericLabelFrom(conditions, value);
else _label = getLabelFrom(conditions, value);
return finalLabel(_label, value);
}

View File

@@ -6,6 +6,7 @@ import {
IPopoverCellProps,
} from "../types";
import _find from "lodash/find";
import { makeStyles, createStyles } from "@mui/styles";
import { Popover, PopoverProps } from "@mui/material";
@@ -50,7 +51,7 @@ export default function withPopoverCell(
return function PopoverCell(props: FormatterProps<any>) {
const classes = useStyles();
const { transparent, ...popoverProps } = options ?? {};
const { updateCell } = useProjectContext();
const { deleteCell, updateCell, tableState } = useProjectContext();
const { validationRegex, required } = (props.column as any).config;
@@ -98,8 +99,18 @@ export default function withPopoverCell(
</ErrorBoundary>
);
//This is where we update the documents
const handleSubmit = (value: any) => {
if (updateCell && !options?.readOnly) {
const targetRow = _find(tableState?.rows, { id: props.row.ref.id });
const targetCell = targetRow?.[props.column.key];
const canDelete = Boolean(
typeof value === "undefined" && targetCell !== value
);
if (deleteCell && !options?.readOnly && canDelete) {
deleteCell(props.row.ref, props.column.key);
setLocalValue(value);
} else if (updateCell && !options?.readOnly) {
updateCell(props.row.ref, props.column.key, value);
setLocalValue(value);
}

View File

@@ -61,6 +61,10 @@ export interface IProjectContext {
ignoreRequiredFields?: boolean
) => void;
deleteRow: (rowId) => void;
deleteCell: (
rowRef: firebase.firestore.DocumentReference,
fieldValue: string
) => void;
updateCell: (
ref: firebase.firestore.DocumentReference,
fieldName: string,
@@ -290,6 +294,17 @@ export const ProjectContextProvider: React.FC = ({ children }) => {
return;
};
const deleteCell: IProjectContext["deleteCell"] = (rowRef, fieldValue) => {
rowRef
.update({
[fieldValue]: firebase.firestore.FieldValue.delete(),
})
.then(
() => console.log("Field Value deleted"),
(error) => console.error("Failed to delete", error)
);
};
const updateCell: IProjectContext["updateCell"] = (
ref,
fieldName,
@@ -297,9 +312,7 @@ export const ProjectContextProvider: React.FC = ({ children }) => {
onSuccess
) => {
if (value === undefined) return;
const update = { [fieldName]: value };
if (table?.audit !== false) {
update[table?.auditFieldUpdatedBy || "_updatedBy"] = rowyUser(
currentUser!,
@@ -395,6 +408,7 @@ export const ProjectContextProvider: React.FC = ({ children }) => {
tableActions,
addRow,
addRows,
deleteCell,
updateCell,
deleteRow,
settingsActions,

4
test.js Normal file
View File

@@ -0,0 +1,4 @@
const values = [true, false, undefined, null, "string"];
const randIndx = Math.floor(Math.random() * 6);
values.push(Math.random() * 100);
console.log(values, values[randIndx], randIndx);