@truedat/ai 8.10.3 → 8.10.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -49,10 +49,10 @@ exports[`<Prompts /> matches the latest snapshot 1`] = `
49
49
  class="header"
50
50
  >
51
51
  <div
52
- class="ui grey circular empty label"
52
+ class="ui green circular empty label"
53
53
  />
54
54
 
55
- rm1
55
+ tr1
56
56
  </div>
57
57
  </div>
58
58
  </div>
@@ -1,200 +0,0 @@
1
- import { Fragment, useEffect } from "react";
2
- import PropTypes from "prop-types";
3
- import { useIntl } from "react-intl";
4
- import {
5
- Button,
6
- Container,
7
- Divider,
8
- Grid,
9
- Header,
10
- Form,
11
- Dropdown,
12
- } from "semantic-ui-react";
13
- import { FormProvider, useForm, Controller } from "react-hook-form";
14
- import { ConfirmModal } from "@truedat/core/components";
15
- import { useResourceTypeOptions } from "../constants";
16
- import { selectorFor } from "./selectors";
17
- import ResourceMappingFields from "./ResourceMappingFields";
18
-
19
- export default function ResourceMappingEditor({
20
- selectedResourceMapping,
21
- onSubmit,
22
- onCancel,
23
- onDelete,
24
- isSubmitting,
25
- setDirty,
26
- }) {
27
- const { formatMessage } = useIntl();
28
- const resourceTypeOptions = useResourceTypeOptions();
29
- const form = useForm({
30
- mode: "onTouched",
31
- defaultValues: selectedResourceMapping,
32
- });
33
- const { control, handleSubmit, watch, formState } = form;
34
- const { isDirty, isValid } = formState;
35
-
36
- useEffect(() => {
37
- setDirty(isDirty);
38
- }, [setDirty, isDirty]);
39
-
40
- if (!selectedResourceMapping) return null;
41
-
42
- const name =
43
- watch("name") ||
44
- formatMessage({
45
- id: "resourceMappings.form.name.new",
46
- });
47
- const resourceType = watch("resource_type");
48
-
49
- return (
50
- <Fragment>
51
- <FormProvider {...form}>
52
- <Form>
53
- <Header as="h3" dividing>
54
- {name}
55
- </Header>
56
- <Controller
57
- control={control}
58
- name="name"
59
- rules={{
60
- required: formatMessage(
61
- { id: "form.validation.required" },
62
- {
63
- prop: formatMessage({
64
- id: "resourceMappings.form.name",
65
- }),
66
- },
67
- ),
68
- }}
69
- render={({
70
- field: { onBlur, onChange, value },
71
- fieldState: { error },
72
- }) => (
73
- <Form.Input
74
- autoComplete="off"
75
- placeholder={formatMessage({
76
- id: "resourceMappings.form.name",
77
- })}
78
- error={error?.message}
79
- label={formatMessage({
80
- id: "resourceMappings.form.name",
81
- })}
82
- onBlur={onBlur}
83
- onChange={(_e, { value }) => onChange(value)}
84
- value={value}
85
- required
86
- />
87
- )}
88
- />
89
- <Controller
90
- control={control}
91
- name="resource_type"
92
- rules={{
93
- required: formatMessage(
94
- { id: "form.validation.required" },
95
- {
96
- prop: formatMessage({
97
- id: "resourceMappings.form.resource_type",
98
- }),
99
- },
100
- ),
101
- }}
102
- render={({ field: { onBlur, onChange, value } }) => (
103
- <Form.Field required>
104
- <label>
105
- {formatMessage({
106
- id: "resourceMappings.form.resource_type",
107
- })}
108
- </label>
109
- <Dropdown
110
- selection
111
- onBlur={onBlur}
112
- options={resourceTypeOptions}
113
- onChange={(_e, { value }) => onChange(value)}
114
- value={value}
115
- />
116
- </Form.Field>
117
- )}
118
- />
119
- <Header as="h5" dividing>
120
- {formatMessage({ id: "resourceMappings.form.selector" })}
121
- </Header>
122
- <Grid>
123
- <Grid.Row>
124
- <Grid.Column />
125
- <Grid.Column width={15}>{selectorFor(resourceType)}</Grid.Column>
126
- </Grid.Row>
127
- </Grid>
128
- <Header as="h5" dividing>
129
- {formatMessage({ id: "resourceMappings.form.fields" })}
130
- </Header>
131
- <ResourceMappingFields />
132
- <Divider hidden />
133
- <Container textAlign="right">
134
- <Button
135
- onClick={handleSubmit(onSubmit)}
136
- primary
137
- loading={isSubmitting}
138
- disabled={!isValid || !isDirty}
139
- content={formatMessage({ id: "actions.save" })}
140
- />
141
- {isDirty ? (
142
- <ConfirmModal
143
- trigger={
144
- <Button
145
- content={formatMessage({ id: "actions.cancel" })}
146
- disabled={isSubmitting}
147
- />
148
- }
149
- header={formatMessage({
150
- id: "actions.discard.confirmation.header",
151
- })}
152
- content={formatMessage({
153
- id: "actions.discard.confirmation.content",
154
- })}
155
- onConfirm={onCancel}
156
- onOpen={(e) => e.stopPropagation()}
157
- onClose={(e) => e.stopPropagation()}
158
- />
159
- ) : (
160
- <Button
161
- content={formatMessage({ id: "actions.cancel" })}
162
- disabled={isSubmitting}
163
- onClick={onCancel}
164
- />
165
- )}
166
- {onDelete ? (
167
- <ConfirmModal
168
- trigger={
169
- <Button
170
- color="red"
171
- content={formatMessage({ id: "actions.delete" })}
172
- disabled={isSubmitting}
173
- />
174
- }
175
- header={formatMessage({
176
- id: "functions.action.delete.header",
177
- })}
178
- content={formatMessage({
179
- id: "functions.action.delete.content",
180
- })}
181
- onConfirm={onDelete}
182
- onOpen={(e) => e.stopPropagation()}
183
- onClose={(e) => e.stopPropagation()}
184
- />
185
- ) : null}
186
- </Container>
187
- </Form>
188
- </FormProvider>
189
- </Fragment>
190
- );
191
- }
192
-
193
- ResourceMappingEditor.propTypes = {
194
- selectedResourceMapping: PropTypes.object,
195
- onSubmit: PropTypes.func,
196
- onCancel: PropTypes.func,
197
- onDelete: PropTypes.func,
198
- isSubmitting: PropTypes.bool,
199
- setDirty: PropTypes.func,
200
- };
@@ -1,120 +0,0 @@
1
- import { useState } from "react";
2
- import PropTypes from "prop-types";
3
- import { useIntl } from "react-intl";
4
- import { Controller, useFormContext, useFieldArray } from "react-hook-form";
5
- import { Button, Grid, Form, List } from "semantic-ui-react";
6
-
7
- export default function ResourceMappingFields() {
8
- const { formatMessage } = useIntl();
9
- const { fields, append, remove } = useFieldArray({
10
- name: "fields",
11
- keyName: "key",
12
- });
13
-
14
- const newField = () => ({
15
- source: "",
16
- target: "",
17
- });
18
-
19
- return (
20
- <List>
21
- {fields.map((field, index) => (
22
- <List.Item key={field.key}>
23
- <FieldItem index={index} onDelete={() => remove(index)} />
24
- </List.Item>
25
- ))}
26
- <List.Item>
27
- <Button onClick={() => append(newField())}>
28
- {formatMessage({ id: "resourceMappings.form.fields.add" })}
29
- </Button>
30
- </List.Item>
31
- </List>
32
- );
33
- }
34
-
35
- const FieldItem = ({ index, onDelete }) => {
36
- const { formatMessage } = useIntl();
37
- const { control } = useFormContext();
38
- const [isShownDelete, setIsShownDelete] = useState();
39
-
40
- return (
41
- <div
42
- onMouseEnter={() => setIsShownDelete(true)}
43
- onMouseLeave={() => setIsShownDelete(false)}
44
- >
45
- <Grid>
46
- <Grid.Row>
47
- <Grid.Column />
48
- <Grid.Column width={15}>
49
- <Grid.Row>
50
- <Form.Group inline className="no-margin">
51
- <Controller
52
- control={control}
53
- name={`fields[${index}].source`}
54
- rules={{
55
- required: formatMessage(
56
- { id: "form.validation.required" },
57
- {
58
- prop: formatMessage({
59
- id: "resourceMappings.form.fields.source",
60
- }),
61
- },
62
- ),
63
- }}
64
- render={({
65
- field: { onBlur, onChange, value },
66
- fieldState: { error },
67
- }) => (
68
- <Form.Input
69
- autoComplete="off"
70
- placeholder={formatMessage({
71
- id: "resourceMappings.form.fields.source",
72
- })}
73
- error={error?.message}
74
- onBlur={onBlur}
75
- onChange={(_e, { value }) => onChange(value)}
76
- value={value}
77
- />
78
- )}
79
- />
80
- <Controller
81
- control={control}
82
- name={`fields[${index}].target`}
83
- render={({
84
- field: { onBlur, onChange, value },
85
- fieldState: { error },
86
- }) => (
87
- <Form.Input
88
- autoComplete="off"
89
- placeholder={formatMessage({
90
- id: "resourceMappings.form.fields.target",
91
- })}
92
- error={error?.message}
93
- onBlur={onBlur}
94
- onChange={(_e, { value }) => onChange(value)}
95
- value={value || ""}
96
- />
97
- )}
98
- />
99
- {isShownDelete ? (
100
- <Button
101
- onClick={onDelete}
102
- icon="trash alternate outline"
103
- basic
104
- color="red"
105
- aria-label="delete"
106
- />
107
- ) : null}
108
- </Form.Group>
109
- </Grid.Row>
110
- </Grid.Column>
111
- </Grid.Row>
112
- </Grid>
113
- </div>
114
- );
115
- };
116
-
117
- FieldItem.propTypes = {
118
- index: PropTypes.number,
119
- onDelete: PropTypes.func,
120
- };
@@ -1,148 +0,0 @@
1
- import _ from "lodash/fp";
2
- import { useState } from "react";
3
- import { useIntl, FormattedMessage } from "react-intl";
4
- import {
5
- Button,
6
- Grid,
7
- GridColumn,
8
- Header,
9
- Icon,
10
- List,
11
- Segment,
12
- } from "semantic-ui-react";
13
-
14
- import {
15
- useResourceMappings,
16
- useResourceMappingCreate,
17
- useResourceMappingDelete,
18
- useResourceMappingUpdate,
19
- } from "@truedat/ai/hooks/useResourceMappings";
20
- import ResourceMappingEditor from "./ResourceMappingEditor";
21
-
22
- const NEW_RESOURCE_MAPPING = {
23
- name: "",
24
- resource_type: "data_structure",
25
- fields: [],
26
- selector: {},
27
- };
28
-
29
- export default function ResourceMappings() {
30
- const { formatMessage } = useIntl();
31
- const { data, loading, mutate } = useResourceMappings();
32
- const [selectedResourceMapping, setSelectedResourceMapping] = useState();
33
- const [isDirty, setDirty] = useState(false);
34
-
35
- const resourceMappings = data?.data;
36
-
37
- const setStateNewResourceMapping = () =>
38
- setSelectedResourceMapping(NEW_RESOURCE_MAPPING);
39
- const clearForm = () => {
40
- setSelectedResourceMapping(null);
41
- setDirty(false);
42
- };
43
-
44
- const { trigger: createResourceMapping, isMutating: isCreating } =
45
- useResourceMappingCreate();
46
-
47
- const { trigger: updateResourceMapping, isMutating: isUpdating } =
48
- useResourceMappingUpdate(selectedResourceMapping);
49
-
50
- const { trigger: deleteResourceMapping, isMutating: isDeleting } =
51
- useResourceMappingDelete(selectedResourceMapping);
52
-
53
- const isSubmitting = isCreating || isUpdating || isDeleting;
54
-
55
- const onResourceMappingCreate = async (resource_mapping) => {
56
- const mutateResourceMapping = selectedResourceMapping?.id
57
- ? updateResourceMapping
58
- : createResourceMapping;
59
- await mutateResourceMapping({ resource_mapping });
60
- clearForm();
61
- mutate();
62
- };
63
-
64
- const onResourceMappingDelete = async (resource_mapping) => {
65
- await deleteResourceMapping({ resource_mapping });
66
- clearForm();
67
- mutate();
68
- };
69
-
70
- return (
71
- <Segment loading={loading}>
72
- <Header as="h2">
73
- <Icon
74
- circular
75
- name={formatMessage({
76
- id: "resourceMappings.header.icon",
77
- defaultMessage: "map signs",
78
- })}
79
- />
80
- <Header.Content>
81
- <FormattedMessage id="resourceMappings.header" />
82
- <Header.Subheader>
83
- <FormattedMessage id="resourceMappings.subheader" />
84
- </Header.Subheader>
85
- </Header.Content>
86
- </Header>
87
- <Grid>
88
- <GridColumn width={4}>
89
- <Button fluid onClick={setStateNewResourceMapping} disabled={isDirty}>
90
- {formatMessage({ id: "resourceMappings.action.new" })}
91
- </Button>
92
- <List divided selection={!isDirty}>
93
- {!_.isEmpty(resourceMappings) ? (
94
- resourceMappings.map((resourceMapping, key) => (
95
- <List.Item
96
- key={key}
97
- onClick={() =>
98
- !isDirty && setSelectedResourceMapping(resourceMapping)
99
- }
100
- >
101
- <List.Content>
102
- <List.Header>
103
- {resourceMapping.name ||
104
- formatMessage({
105
- id: "resourceMappings.form.name.new",
106
- })}
107
- </List.Header>
108
- </List.Content>
109
- </List.Item>
110
- ))
111
- ) : (
112
- <List.Item>
113
- <List.Content>
114
- <List.Header>
115
- {formatMessage({ id: "resourceMappings.empty_list" })}
116
- </List.Header>
117
- </List.Content>
118
- </List.Item>
119
- )}
120
- </List>
121
- </GridColumn>
122
- <GridColumn width={11}>
123
- {selectedResourceMapping ? (
124
- <ResourceMappingEditor
125
- key={selectedResourceMapping?.id || "new"}
126
- selectedResourceMapping={selectedResourceMapping}
127
- resourceMappings={resourceMappings}
128
- onCancel={clearForm}
129
- onSubmit={onResourceMappingCreate}
130
- onDelete={
131
- selectedResourceMapping?.id ? onResourceMappingDelete : null
132
- }
133
- isSubmitting={isSubmitting}
134
- setDirty={setDirty}
135
- />
136
- ) : (
137
- <Header as="h2" icon textAlign="center">
138
- <Icon name="hand pointer outline" />
139
- <Header.Subheader>
140
- {formatMessage({ id: "resourceMappings.no_selection" })}
141
- </Header.Subheader>
142
- </Header>
143
- )}
144
- </GridColumn>
145
- </Grid>
146
- </Segment>
147
- );
148
- }
@@ -1,163 +0,0 @@
1
- import userEvent from "@testing-library/user-event";
2
- import { waitFor } from "@testing-library/react";
3
- import { render, waitForLoad } from "@truedat/test/render";
4
- import ResourceMappingEditor from "../ResourceMappingEditor";
5
-
6
- const props = {
7
- selectedResourceMapping: {
8
- id: 1,
9
- name: "rm1",
10
- resource_type: "boolean",
11
- fields: [{ source: "s1", target: "t1" }],
12
- selector: { system_external_id: 1 },
13
- },
14
- resourceMappings: [
15
- {
16
- id: 1,
17
- name: "rm1",
18
- resource_type: "boolean",
19
- fields: [{ source: "s1", target: "t1" }],
20
- selector: { system_external_id: 1 },
21
- },
22
- ],
23
- onSubmit: jest.fn(),
24
- onCancel: jest.fn(),
25
- onDelete: jest.fn(),
26
- isSubmitting: false,
27
- setDirty: jest.fn(),
28
- };
29
-
30
- describe("<ResourceMappingEditor />", () => {
31
- it("matches the latest snapshot", async () => {
32
- const rendered = render(<ResourceMappingEditor {...props} />);
33
- await waitForLoad(rendered);
34
-
35
- expect(rendered.container).toMatchSnapshot();
36
- });
37
-
38
- it("matches snapshot without selected resource mapping", async () => {
39
- const props = { setDirty: jest.fn() };
40
- const rendered = render(<ResourceMappingEditor {...props} />);
41
- await waitForLoad(rendered);
42
-
43
- expect(rendered.container).toMatchSnapshot();
44
- });
45
-
46
- it("matches snapshot without onDelete", async () => {
47
- const thisProps = {
48
- ...props,
49
- onDelete: null,
50
- };
51
- const rendered = render(<ResourceMappingEditor {...thisProps} />);
52
- await waitForLoad(rendered);
53
-
54
- expect(rendered.container).toMatchSnapshot();
55
- });
56
-
57
- it("test cancel button", async () => {
58
- const onCancel = jest.fn();
59
- const thisProps = { ...props, onCancel };
60
- const rendered = render(<ResourceMappingEditor {...thisProps} />);
61
- await waitForLoad(rendered);
62
-
63
- const user = userEvent.setup({ delay: null });
64
- await user.click(rendered.getByRole("button", { name: /cancel/i }));
65
-
66
- await waitFor(() => expect(onCancel).toHaveBeenCalled());
67
- });
68
-
69
- it("test cancel button with confirm", async () => {
70
- const onCancel = jest.fn();
71
- const thisProps = { ...props, onCancel };
72
- const rendered = render(<ResourceMappingEditor {...thisProps} />);
73
- await waitForLoad(rendered);
74
-
75
- const user = userEvent.setup({ delay: null });
76
- await user.type(rendered.getAllByRole("textbox")[0], "name");
77
-
78
- await waitFor(() =>
79
- expect(rendered.getByRole("button", { name: /save/i })).toBeEnabled(),
80
- );
81
-
82
- await user.click(rendered.getByRole("button", { name: /cancel/i }));
83
- expect(onCancel).toHaveBeenCalledTimes(0);
84
-
85
- await user.click(
86
- rendered.getByRole("button", { name: /modal-negative-action/i }),
87
- );
88
- expect(onCancel).toHaveBeenCalledTimes(0);
89
-
90
- await user.click(rendered.getByRole("button", { name: /cancel/i }));
91
- expect(onCancel).toHaveBeenCalledTimes(0);
92
-
93
- expect(rendered.container).toMatchSnapshot();
94
-
95
- await user.click(
96
- rendered.getByRole("button", { name: /modal-affirmative-action/i }),
97
- );
98
- expect(onCancel).toHaveBeenCalledTimes(1);
99
- });
100
-
101
- it("test delete button", async () => {
102
- const onDelete = jest.fn();
103
- const thisProps = { ...props, onDelete };
104
- const rendered = render(<ResourceMappingEditor {...thisProps} />);
105
- await waitForLoad(rendered);
106
-
107
- const user = userEvent.setup({ delay: null });
108
- await user.click(rendered.getByRole("button", { name: /delete/i }));
109
- expect(onDelete).toHaveBeenCalledTimes(0);
110
-
111
- await user.click(
112
- rendered.getByRole("button", { name: /modal-negative-action/i }),
113
- );
114
- expect(onDelete).toHaveBeenCalledTimes(0);
115
-
116
- await user.click(rendered.getByRole("button", { name: /delete/i }));
117
- expect(onDelete).toHaveBeenCalledTimes(0);
118
-
119
- expect(rendered.container).toMatchSnapshot();
120
-
121
- await user.click(
122
- rendered.getByRole("button", { name: /modal-affirmative-action/i }),
123
- );
124
- expect(onDelete).toHaveBeenCalledTimes(1);
125
- });
126
-
127
- it("test submit", async () => {
128
- const onSubmit = jest.fn();
129
- const thisProps = { ...props, onSubmit };
130
- const rendered = render(<ResourceMappingEditor {...thisProps} />);
131
- await waitForLoad(rendered);
132
-
133
- expect(rendered.getByRole("button", { name: /save/i })).toBeDisabled();
134
-
135
- const user = userEvent.setup({ delay: null });
136
- await user.type(rendered.getAllByRole("textbox")[0], "name");
137
-
138
- await waitFor(() =>
139
- expect(rendered.getByRole("button", { name: /save/i })).toBeEnabled(),
140
- );
141
-
142
- await user.click(rendered.getByRole("button", { name: /save/i }));
143
-
144
- await waitFor(() =>
145
- expect(rendered.getByRole("button", { name: /save/i })).toBeEnabled(),
146
- );
147
-
148
- expect(onSubmit.mock.calls[0][0]).toEqual({
149
- fields: [
150
- {
151
- source: "s1",
152
- target: "t1",
153
- },
154
- ],
155
- id: 1,
156
- name: "rm1name",
157
- resource_type: "boolean",
158
- selector: {
159
- system_external_id: 1,
160
- },
161
- });
162
- });
163
- });