Skip to content

Commit cddb44f

Browse files
committed
Support removing all conditions in filters
1 parent a46fa4e commit cddb44f

File tree

3 files changed

+66
-44
lines changed

3 files changed

+66
-44
lines changed

services/backend-api/client/src/features/feedConnections/components/FiltersForm/LogicalExpressionForm.tsx

Lines changed: 58 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@ import {
55
Button,
66
CloseButton,
77
Flex,
8+
FormControl,
9+
FormErrorMessage,
810
Menu,
911
MenuButton,
1012
MenuItem,
1113
MenuList,
1214
Stack,
1315
Text,
1416
} from "@chakra-ui/react";
15-
import { useFieldArray, useFormContext, useWatch } from "react-hook-form";
17+
import { FieldError, useFieldArray, useFormContext, useWatch } from "react-hook-form";
1618
import { useTranslation } from "react-i18next";
19+
import { useEffect } from "react";
1720
import {
1821
FilterExpression,
1922
FilterExpressionType,
@@ -26,6 +29,7 @@ import {
2629
} from "../../types";
2730
import { AnyAllSelector } from "./AnyAllSelector";
2831
import { Condition } from "./Condition";
32+
import { getNestedField } from "../../../../utils/getNestedField";
2933

3034
interface Props {
3135
onDeleted: () => void;
@@ -34,15 +38,34 @@ interface Props {
3438
}
3539

3640
export const LogicalExpressionForm = ({ onDeleted, prefix = "", containerProps }: Props) => {
37-
const { control, setValue } = useFormContext();
41+
const {
42+
control,
43+
setValue,
44+
setError,
45+
clearErrors,
46+
formState: { errors },
47+
} = useFormContext();
48+
const childrenName = `${prefix}children`;
49+
const childrenError = getNestedField<FieldError>(errors, childrenName);
3850
const { fields, append, remove, insert } = useFieldArray({
3951
control,
40-
name: `${prefix}children`,
52+
name: childrenName,
4153
});
4254
const operator: LogicalExpressionOperator = useWatch({
4355
control,
4456
name: `${prefix}op`,
4557
});
58+
// console.log("🚀 ~ LogicalExpressionForm ~ errors:", errors);
59+
// console.log("🚀 ~ useEffect ~ childrenName:", childrenName);
60+
useEffect(() => {
61+
if (fields.length === 0) {
62+
setError(childrenName, {
63+
type: "required",
64+
});
65+
} else if (childrenError?.type === "required") {
66+
clearErrors(childrenName);
67+
}
68+
}, [fields.length, childrenError?.type]);
4669

4770
const { t } = useTranslation();
4871

@@ -111,15 +134,6 @@ export const LogicalExpressionForm = ({ onDeleted, prefix = "", containerProps }
111134
append(toInsert);
112135
};
113136

114-
const numberOfRelational =
115-
(
116-
fields as Array<
117-
FilterExpression & {
118-
id: string;
119-
}
120-
>
121-
)?.filter((child) => child?.type === FilterExpressionType.Relational).length || 0;
122-
123137
return (
124138
<Stack {...containerProps}>
125139
<Box
@@ -144,35 +158,40 @@ export const LogicalExpressionForm = ({ onDeleted, prefix = "", containerProps }
144158
<CloseButton aria-label="Delete condition group" onClick={onDeleted} />
145159
</Flex>
146160
<Stack>
147-
<Stack spacing={2} marginTop={4} width="100%">
148-
{(fields as Array<FilterExpression & { id: string }>)?.map((child, childIndex) => {
149-
if (child?.type === FilterExpressionType.Logical) {
150-
return (
151-
<LogicalExpressionForm
152-
key={child.id}
153-
onDeleted={() => onChildDeleted(childIndex)}
154-
prefix={`${prefix}children.${childIndex}.`}
155-
/>
156-
);
157-
}
158-
159-
if (child?.type === FilterExpressionType.Relational) {
160-
return (
161-
<Condition
162-
key={child.id}
163-
onDelete={() => onChildDeleted(childIndex)}
164-
prefix={`${prefix}children.${childIndex}.`}
165-
deletable={numberOfRelational > 1}
166-
/>
167-
);
168-
}
169-
170-
return null;
171-
})}
172-
</Stack>
161+
{!!fields.length && (
162+
<Stack spacing={2} marginTop={4} width="100%">
163+
{(fields as Array<FilterExpression & { id: string }>)?.map((child, childIndex) => {
164+
if (child?.type === FilterExpressionType.Logical) {
165+
return (
166+
<LogicalExpressionForm
167+
key={child.id}
168+
onDeleted={() => onChildDeleted(childIndex)}
169+
prefix={`${prefix}children.${childIndex}.`}
170+
/>
171+
);
172+
}
173+
174+
if (child?.type === FilterExpressionType.Relational) {
175+
return (
176+
<Condition
177+
key={child.id}
178+
onDelete={() => onChildDeleted(childIndex)}
179+
prefix={`${prefix}children.${childIndex}.`}
180+
deletable
181+
/>
182+
);
183+
}
184+
185+
return null;
186+
})}
187+
</Stack>
188+
)}
189+
<FormControl isInvalid={childrenError?.type === "required"}>
190+
<FormErrorMessage my={4}>At least one condition is required.</FormErrorMessage>
191+
</FormControl>
173192
<Box>
174193
<Menu>
175-
<MenuButton as={Button} rightIcon={<ChevronDownIcon />} variant="ghost">
194+
<MenuButton as={Button} rightIcon={<ChevronDownIcon />}>
176195
{t("features.feedConnections.components.filtersForm.addButtonText")}
177196
</MenuButton>
178197
<MenuList>

services/backend-api/client/src/features/feedConnections/components/FiltersForm/index.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export const FiltersForm = ({
4949
resetField,
5050
reset,
5151
} = formMethods;
52+
5253
// @ts-ignore cyclical references in typescript types
5354
const watchedExpression = useWatch({
5455
control,
@@ -67,10 +68,12 @@ export const FiltersForm = ({
6768
};
6869

6970
const onSaveExpression = async ({ expression: finalExpression }: FormData) => {
70-
await onSave(finalExpression);
71-
reset({
72-
expression: finalExpression,
73-
});
71+
try {
72+
await onSave(finalExpression);
73+
reset({
74+
expression: finalExpression,
75+
});
76+
} catch (err) {}
7477
};
7578

7679
const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {

services/backend-api/client/src/mocks/handlers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -873,7 +873,7 @@ const handlers = [
873873
rest.patch("/api/v1/user-feeds/:feedId/connections/discord-channels/:id", (req, res, ctx) => {
874874
return res(
875875
ctx.delay(500),
876-
ctx.status(400),
876+
ctx.status(200),
877877
ctx.json<UpdateDiscordChannelConnectionOutput>({
878878
result: mockFeedChannelConnections[0],
879879
})

0 commit comments

Comments
 (0)