Skip to content

Commit 760b491

Browse files
authored
Fix rules match forms - IFC-1563 (#6638)
* query trigger rule to get node kind * move files, update const * fix fields re render to unset values on trigger change * fix attribute form * fix alerts * start add test * prevent dissociate in table toolabr if not possible * add comment * add check for pre filled value * fix test * update renderMore * check parentSchema * update query and types * fix initial state for edit * fix test * fix test * fix test
1 parent 1faf046 commit 760b491

File tree

9 files changed

+353
-91
lines changed

9 files changed

+353
-91
lines changed

frontend/app/src/entities/nodes/relationships/ui/relationship-table/relationship-table.tsx

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import {
66
} from "@/entities/nodes/relationships/domain/get-object-relationships/get-object-relationships.query";
77
import { getRelationshipActionsColumn } from "@/entities/nodes/relationships/ui/relationship-table/get-relationship-actions-column";
88
import { ToolbarDissociateAction } from "@/entities/nodes/relationships/ui/relationship-table/toolbar-dissociate-action";
9+
import { canDissociateRelationship } from "@/entities/nodes/relationships/utils/can-dissociate-relationship";
910
import { PERMISSION_ALLOW_ALL } from "@/entities/permission/constants";
11+
import { useSchema } from "@/entities/schema/ui/hooks/useSchema";
1012
import { DataTable } from "@/shared/components/table/data-table";
1113
import { InfiniteScroll } from "@/shared/components/utils/infinite-scroll";
1214
import useFilters from "@/shared/hooks/useFilters";
@@ -21,6 +23,7 @@ export function RelationshipTable({
2123
parentKind,
2224
...props
2325
}: RelationshipTableProps) {
26+
const { schema: parentSchema } = useSchema(parentKind);
2427
const [filters] = useFilters();
2528
const { data, fetchNextPage, hasNextPage, isPending, isFetchingNextPage } =
2629
useObjectRelationships({
@@ -49,21 +52,35 @@ export function RelationshipTable({
4952

5053
const isLoading = isPending || isFetchingNextPage;
5154

55+
const isDissociateAllowed =
56+
parentSchema &&
57+
canDissociateRelationship({
58+
parentSchema,
59+
relationshipName,
60+
relationshipsCount: flatData.length,
61+
});
62+
5263
return (
5364
<InfiniteScroll scrollX hasNextPage={hasNextPage} onLoadMore={fetchNextPage}>
5465
<DataTable
5566
columns={columns}
5667
data={flatData}
5768
isLoading={isLoading}
5869
renderEmpty={() => <ObjectTableEmpty schema={relationshipSchema} />}
59-
toolbarActions={({ selectedRows }) => (
60-
<ToolbarDissociateAction
61-
objectId={parentId}
62-
relationshipIds={selectedRows.map((row) => row.id)}
63-
relationshipName={relationshipName}
64-
relationshipLabel="all selected rows"
65-
/>
66-
)}
70+
toolbarActions={
71+
isDissociateAllowed
72+
? ({ selectedRows }) => {
73+
return (
74+
<ToolbarDissociateAction
75+
objectId={parentId}
76+
relationshipIds={selectedRows.map((row) => row.id)}
77+
relationshipName={relationshipName}
78+
relationshipLabel="all selected rows"
79+
/>
80+
);
81+
}
82+
: undefined
83+
}
6784
/>
6885
</InfiniteScroll>
6986
);
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { NODE_TRIGGER_RULE } from "@/entities/triggers/constants";
2+
import graphqlClient from "@/shared/api/graphql/graphqlClientApollo";
3+
import { ContextParams } from "@/shared/api/types";
4+
import { gql } from "@apollo/client";
5+
6+
const GET_MATCH_PARENT = gql`
7+
query GetMatchParent($objectsId: [ID]) {
8+
[${NODE_TRIGGER_RULE}]: {
9+
__args: {
10+
matches__ids: $objectsId,
11+
},
12+
edges: {
13+
node: {
14+
id
15+
node_kind: {
16+
value
17+
}
18+
}
19+
}
20+
}
21+
}
22+
}
23+
`;
24+
25+
export interface GetMatchParentParams extends ContextParams {
26+
objectId: string;
27+
}
28+
29+
export const getMatchParentFromApi = async ({
30+
branchName,
31+
atDate,
32+
objectId,
33+
}: GetMatchParentParams) => {
34+
return graphqlClient.query({
35+
query: GET_MATCH_PARENT,
36+
variables: {
37+
objectsId: [objectId],
38+
},
39+
context: {
40+
branch: branchName,
41+
date: atDate,
42+
},
43+
});
44+
};
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
export const NODE_TRIGGER_RULE = "CoreNodeTriggerRule";
12
export const NODE_TRIGGER_ATTRIBUTE_MATCH = "CoreNodeTriggerAttributeMatch";
2-
export const NODE_TRIGGER_RELATIONSHIP = "CoreNodeTriggerRelationshipMatch";
3+
export const NODE_TRIGGER_RELATIONSHIP_MATCH = "CoreNodeTriggerRelationshipMatch";
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { useCurrentBranch } from "@/entities/branches/ui/branches-provider";
2+
import { GetMatchParentParams, getMatchParent } from "@/entities/triggers/domain/get-match-parent";
3+
import { ContextParams } from "@/shared/api/types";
4+
import { datetimeAtom } from "@/shared/stores/time.atom";
5+
import { queryOptions, useQuery } from "@tanstack/react-query";
6+
import { useAtomValue } from "jotai";
7+
8+
export function getMatchParentQueryOptions(params: GetMatchParentParams) {
9+
return queryOptions({
10+
queryKey: [params.branchName, params.atDate, "objects", params.objectId],
11+
queryFn: () => getMatchParent(params),
12+
});
13+
}
14+
15+
export function useGetMatchParent(params: Omit<GetMatchParentParams, keyof ContextParams>) {
16+
const { currentBranch } = useCurrentBranch();
17+
const timeMachineDate = useAtomValue(datetimeAtom);
18+
19+
return useQuery(
20+
getMatchParentQueryOptions({
21+
...params,
22+
branchName: currentBranch.name,
23+
atDate: timeMachineDate,
24+
})
25+
);
26+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { NodeObject } from "@/entities/nodes/types";
2+
import { getMatchParentFromApi } from "@/entities/triggers/api/get-match-parent-from-api";
3+
import { ContextParams } from "@/shared/api/types";
4+
5+
export interface GetMatchParentParams extends ContextParams {
6+
objectId: string;
7+
}
8+
9+
export type GetObject = (
10+
params: GetMatchParentParams
11+
) => Promise<{ id: string; node_kind: { value: string } }>;
12+
13+
export const getMatchParent: GetObject = async ({ branchName, atDate, objectId }) => {
14+
const { data } = await getMatchParentFromApi({ branchName, objectId, atDate });
15+
16+
const result =
17+
data?.CoreNodeTriggerRule?.edges?.map((edge: { node: NodeObject }) => edge.node) ?? [];
18+
19+
if (!result || result.length === 0) {
20+
throw new Error(`Cannot find Trigger Rule with id ${objectId}`);
21+
}
22+
23+
return result[0];
24+
};

frontend/app/src/entities/triggers/ui/node-attribute-match-form.tsx

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,36 @@
1+
import { useCurrentBranch } from "@/entities/branches/ui/branches-provider";
12
import { createObject } from "@/entities/nodes/api/createObject";
23
import { updateObjectWithId } from "@/entities/nodes/api/updateObjectWithId";
4+
import { AttributeType, RelationshipType } from "@/entities/nodes/getObjectItemDisplayValue";
5+
import { useGetObject } from "@/entities/nodes/object/domain/get-object.query";
6+
import { useSchema } from "@/entities/schema/ui/hooks/useSchema";
7+
import { NODE_TRIGGER_ATTRIBUTE_MATCH, NODE_TRIGGER_RULE } from "@/entities/triggers/constants";
38
import graphqlClient from "@/shared/api/graphql/graphqlClientApollo";
49
import { Button } from "@/shared/components/buttons/button-primitive";
10+
import { DynamicInput } from "@/shared/components/form/dynamic-form";
11+
import { LabelFormField } from "@/shared/components/form/fields/common";
12+
import DropdownField from "@/shared/components/form/fields/dropdown.field";
513
import { NodeFormProps } from "@/shared/components/form/node-form";
6-
import { DynamicDropdownFieldProps, FormFieldValue } from "@/shared/components/form/type";
14+
import {
15+
DynamicDropdownFieldProps,
16+
FormAttributeValue,
17+
FormFieldValue,
18+
} from "@/shared/components/form/type";
719
import { getCurrentFieldValue } from "@/shared/components/form/utils/getFieldDefaultValue";
20+
import { getFormFieldsFromSchema } from "@/shared/components/form/utils/getFormFieldsFromSchema";
21+
import { getRelationshipDefaultValue } from "@/shared/components/form/utils/getRelationshipDefaultValue";
822
import { getCreateMutationFromFormDataOnly } from "@/shared/components/form/utils/mutations/getCreateMutationFromFormData";
23+
import { DropdownOption } from "@/shared/components/inputs/dropdown";
24+
import { Skeleton } from "@/shared/components/skeleton";
925
import { ALERT_TYPES, Alert } from "@/shared/components/ui/alert";
1026
import { Form, FormSubmit } from "@/shared/components/ui/form";
1127
import { datetimeAtom } from "@/shared/stores/time.atom";
1228
import { stringifyWithoutQuotes } from "@/shared/utils/string";
1329
import { gql } from "@apollo/client";
1430
import { useAtomValue } from "jotai";
15-
import { FieldValues, useForm } from "react-hook-form";
31+
import { FieldValues, useForm, useFormContext } from "react-hook-form";
1632
import { toast } from "react-toastify";
1733

18-
import { useCurrentBranch } from "@/entities/branches/ui/branches-provider";
19-
import { AttributeType, RelationshipType } from "@/entities/nodes/getObjectItemDisplayValue";
20-
import { useGetObject } from "@/entities/nodes/object/domain/get-object.query";
21-
import { useSchema } from "@/entities/schema/ui/hooks/useSchema";
22-
import { DynamicInput } from "@/shared/components/form/dynamic-form";
23-
import { LabelFormField } from "@/shared/components/form/fields/common";
24-
import DropdownField from "@/shared/components/form/fields/dropdown.field";
25-
import { getFormFieldsFromSchema } from "@/shared/components/form/utils/getFormFieldsFromSchema";
26-
import { getRelationshipDefaultValue } from "@/shared/components/form/utils/getRelationshipDefaultValue";
27-
import { DropdownOption } from "@/shared/components/inputs/dropdown";
28-
import { Skeleton } from "@/shared/components/skeleton";
29-
import { useParams } from "react-router";
30-
import { NODE_TRIGGER_ATTRIBUTE_MATCH } from "../constants";
31-
3234
interface NodeAttributeMatchFormProps extends NodeFormProps {}
3335

3436
export const NodeAttributeMatchForm = ({
@@ -41,9 +43,6 @@ export const NodeAttributeMatchForm = ({
4143
}: NodeAttributeMatchFormProps) => {
4244
const { currentBranch } = useCurrentBranch();
4345
const date = useAtomValue(datetimeAtom);
44-
const { objectKind, objectid } = useParams();
45-
const { schema } = useSchema(objectKind);
46-
const { data, isPending } = useGetObject({ objectSchema: schema, objectId: objectid });
4746

4847
const schemaFields = getFormFieldsFromSchema({
4948
...props,
@@ -123,7 +122,7 @@ export const NodeAttributeMatchForm = ({
123122
},
124123
});
125124

126-
if (currentObject) {
125+
if (currentObject?.id) {
127126
toast(<Alert type={ALERT_TYPES.SUCCESS} message={"Node attribute match updated!"} />, {
128127
toastId: "alert-success-node-attribute-match-updated",
129128
});
@@ -145,11 +144,7 @@ export const NodeAttributeMatchForm = ({
145144
return (
146145
<div className={"bg-white flex flex-col flex-1 overflow-auto p-4"}>
147146
<Form form={form} onSubmit={handleSubmit}>
148-
<NodeAttributeField
149-
field={attributeField}
150-
kind={data?.node_kind?.value}
151-
isLoading={isPending}
152-
/>
147+
<NodeAttributeField field={attributeField} />
153148

154149
{fields.map((field) => {
155150
return <DynamicInput key={field.name} {...field} />;
@@ -170,15 +165,23 @@ export const NodeAttributeMatchForm = ({
170165
};
171166

172167
interface NodeAttributeFieldProps {
173-
kind?: string;
174-
isLoading?: boolean;
175168
field?: DynamicDropdownFieldProps;
176169
}
177170

178-
const NodeAttributeField = ({ field, kind, isLoading }: NodeAttributeFieldProps) => {
179-
const { schema } = useSchema(kind);
171+
const NodeAttributeField = ({ field }: NodeAttributeFieldProps) => {
172+
const form = useFormContext();
173+
174+
const { schema } = useSchema(NODE_TRIGGER_RULE);
175+
const selectedTriggerField: FormAttributeValue = form.watch("trigger");
176+
177+
const { data, isPending } = useGetObject({
178+
objectId: selectedTriggerField.value?.id,
179+
objectSchema: schema,
180+
});
180181

181-
if (isLoading) {
182+
const { schema: peerSchema } = useSchema(data?.node_kind?.value);
183+
184+
if (isPending) {
182185
return (
183186
<div className="space-y-2">
184187
<LabelFormField
@@ -193,12 +196,19 @@ const NodeAttributeField = ({ field, kind, isLoading }: NodeAttributeFieldProps)
193196
}
194197

195198
const attributeOptions: Array<DropdownOption> =
196-
schema?.attributes?.map((attribute) => {
199+
peerSchema?.attributes?.map((attribute) => {
197200
return {
198201
value: attribute.name,
199202
label: attribute.label ?? attribute.name,
200203
};
201204
}) ?? [];
202205

203-
return <DropdownField {...field} name="attribute_name" items={attributeOptions} />;
206+
return (
207+
<DropdownField
208+
{...field}
209+
key={data?.node_kind?.value}
210+
name="attribute_name"
211+
items={attributeOptions}
212+
/>
213+
);
204214
};

0 commit comments

Comments
 (0)