@truedat/dq 4.33.10 → 4.35.2

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.
Files changed (96) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/package.json +5 -5
  3. package/src/api.js +7 -1
  4. package/src/components/ConditionSummary.js +23 -21
  5. package/src/components/ExecutionDetails.js +11 -14
  6. package/src/components/ExecutionGroup.js +24 -15
  7. package/src/components/ImplementationResultBar.js +80 -0
  8. package/src/components/ImplementationSummary.js +33 -72
  9. package/src/components/ImplementationsUploadButton.js +61 -0
  10. package/src/components/InformationSummary.js +68 -0
  11. package/src/components/NewRuleImplementation.js +12 -0
  12. package/src/components/RuleForm.js +0 -178
  13. package/src/components/RuleImplementation.js +10 -6
  14. package/src/components/RuleImplementationProperties.js +31 -64
  15. package/src/components/RuleImplementationResults.js +87 -53
  16. package/src/components/RuleImplementationsActions.js +3 -59
  17. package/src/components/RuleImplementationsDownload.js +86 -0
  18. package/src/components/RuleImplementationsOptions.js +28 -0
  19. package/src/components/RuleProperties.js +1 -10
  20. package/src/components/RuleResultDecorator.js +43 -26
  21. package/src/components/RuleResultRow.js +4 -1
  22. package/src/components/RuleResultsUpload.js +47 -0
  23. package/src/components/RuleRoutes.js +0 -13
  24. package/src/components/RuleSummary.js +15 -2
  25. package/src/components/RulesActions.js +17 -10
  26. package/src/components/RulesUploadButton.js +58 -0
  27. package/src/components/__tests__/ExecutionGroup.spec.js +11 -7
  28. package/src/components/__tests__/ImplementationResultBar.spec.js +98 -0
  29. package/src/components/__tests__/ImplementationSummary.spec.js +9 -26
  30. package/src/components/__tests__/InformationSummary.spec.js +35 -0
  31. package/src/components/__tests__/NewRuleImplementation.spec.js +1 -1
  32. package/src/components/__tests__/RuleForm.spec.js +0 -191
  33. package/src/components/__tests__/RuleImplementation.spec.js +1 -0
  34. package/src/components/__tests__/RuleImplementationProperties.spec.js +23 -33
  35. package/src/components/__tests__/RuleImplementationsActions.spec.js +0 -9
  36. package/src/components/__tests__/RuleImplementationsOptions.spec.js +18 -0
  37. package/src/components/__tests__/RuleProperties.spec.js +7 -9
  38. package/src/components/__tests__/RuleResultDecorator.spec.js +17 -11
  39. package/src/components/__tests__/RuleResultRow.spec.js +25 -46
  40. package/src/components/__tests__/RuleResultsUpload.spec.js +18 -0
  41. package/src/components/__tests__/RuleRow.spec.js +0 -4
  42. package/src/components/__tests__/RuleSummary.spec.js +6 -6
  43. package/src/components/__tests__/Rules.spec.js +15 -39
  44. package/src/components/__tests__/__snapshots__/ConditionSummary.spec.js.snap +55 -51
  45. package/src/components/__tests__/__snapshots__/ExecutionGroup.spec.js.snap +5 -4
  46. package/src/components/__tests__/__snapshots__/ImplementationResultBar.spec.js.snap +141 -0
  47. package/src/components/__tests__/__snapshots__/ImplementationSummary.spec.js.snap +194 -457
  48. package/src/components/__tests__/__snapshots__/InformationSummary.spec.js.snap +185 -0
  49. package/src/components/__tests__/__snapshots__/NewRuleImplementation.spec.js.snap +6 -0
  50. package/src/components/__tests__/__snapshots__/RuleForm.spec.js.snap +0 -148
  51. package/src/components/__tests__/__snapshots__/RuleImplementation.spec.js.snap +20 -0
  52. package/src/components/__tests__/__snapshots__/RuleImplementationProperties.spec.js.snap +43 -49
  53. package/src/components/__tests__/__snapshots__/RuleImplementationResults.spec.js.snap +63 -61
  54. package/src/components/__tests__/__snapshots__/RuleImplementationsActions.spec.js.snap +1 -7
  55. package/src/components/__tests__/__snapshots__/RuleImplementationsOptions.spec.js.snap +58 -0
  56. package/src/components/__tests__/__snapshots__/RuleProperties.spec.js.snap +0 -1
  57. package/src/components/__tests__/__snapshots__/RuleResultsUpload.spec.js.snap +18 -0
  58. package/src/components/__tests__/__snapshots__/RuleRow.spec.js.snap +0 -28
  59. package/src/components/__tests__/__snapshots__/Rules.spec.js.snap +0 -101
  60. package/src/components/ruleImplementationForm/FiltersGroup.js +1 -0
  61. package/src/components/ruleImplementationForm/InformationForm.js +5 -5
  62. package/src/components/ruleImplementationForm/LimitsForm.js +142 -0
  63. package/src/components/ruleImplementationForm/RuleImplementationForm.js +14 -4
  64. package/src/components/ruleImplementationForm/RuleImplementationRawForm.js +16 -6
  65. package/src/components/ruleImplementationForm/ValueConditions.js +11 -0
  66. package/src/components/ruleImplementationForm/__tests__/LimitsForm.spec.js +186 -0
  67. package/src/components/ruleImplementationForm/__tests__/RuleImplementationRawForm.spec.js +42 -35
  68. package/src/components/ruleImplementationForm/__tests__/__snapshots__/LimitsForm.spec.js.snap +1104 -0
  69. package/src/components/ruleImplementationForm/__tests__/__snapshots__/RuleImplementationForm.spec.js.snap +4 -1
  70. package/src/components/ruleImplementationForm/__tests__/__snapshots__/RuleImplementationRawForm.spec.js.snap +12 -1
  71. package/src/components/ruleImplementationForm/limitsValidation.js +72 -0
  72. package/src/messages/en.js +167 -76
  73. package/src/messages/es.js +292 -185
  74. package/src/reducers/__tests__/rule.spec.js +2 -4
  75. package/src/reducers/__tests__/ruleImplementation.spec.js +2 -0
  76. package/src/reducers/__tests__/ruleImplementations.spec.js +12 -8
  77. package/src/reducers/__tests__/uploadingResults.spec.js +28 -0
  78. package/src/reducers/dqMessage.js +121 -1
  79. package/src/reducers/index.js +6 -0
  80. package/src/reducers/rule.js +0 -3
  81. package/src/reducers/ruleImplementation.js +3 -0
  82. package/src/reducers/ruleImplementationRedirect.js +6 -1
  83. package/src/reducers/ruleImplementations.js +3 -0
  84. package/src/reducers/ruleRedirect.js +5 -0
  85. package/src/reducers/uploadImplementationsFile.js +28 -0
  86. package/src/reducers/uploadRulesFile.js +25 -0
  87. package/src/reducers/uploadingResults.js +16 -0
  88. package/src/routines.js +3 -0
  89. package/src/sagas/__tests__/uploadResults.spec.js +65 -0
  90. package/src/sagas/index.js +9 -0
  91. package/src/sagas/uploadImplementations.js +28 -0
  92. package/src/sagas/uploadResults.js +32 -0
  93. package/src/sagas/uploadRules.js +28 -0
  94. package/src/selectors/getRuleImplementationColumns.js +38 -3
  95. package/src/selectors/ruleColumnsSelector.js +0 -31
  96. package/src/styles/ruleSummary.less +17 -10
@@ -0,0 +1,58 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<RuleImplementationsOptions /> matches the latest loading snapshot 1`] = `
4
+ <Dropdown
5
+ additionLabel="Add "
6
+ additionPosition="top"
7
+ className="button icon group-actions button-update"
8
+ closeOnBlur={true}
9
+ closeOnEscape={true}
10
+ deburr={false}
11
+ direction="left"
12
+ disabled={true}
13
+ floating={true}
14
+ icon="ellipsis vertical"
15
+ minCharacters={1}
16
+ noResultsMessage="No results found."
17
+ openOnFocus={true}
18
+ renderLabel={[Function]}
19
+ searchInput="text"
20
+ selectOnBlur={true}
21
+ selectOnNavigation={true}
22
+ wrapSelection={true}
23
+ >
24
+ <DropdownMenu>
25
+ <Connect(RuleImplementationsDownload) />
26
+ <Connect(ImplementationsUploadButton) />
27
+ <Connect(RuleResultsUpload) />
28
+ </DropdownMenu>
29
+ </Dropdown>
30
+ `;
31
+
32
+ exports[`<RuleImplementationsOptions /> matches the latest snapshot 1`] = `
33
+ <Dropdown
34
+ additionLabel="Add "
35
+ additionPosition="top"
36
+ className="button icon group-actions button-update"
37
+ closeOnBlur={true}
38
+ closeOnEscape={true}
39
+ deburr={false}
40
+ direction="left"
41
+ floating={true}
42
+ icon="ellipsis vertical"
43
+ minCharacters={1}
44
+ noResultsMessage="No results found."
45
+ openOnFocus={true}
46
+ renderLabel={[Function]}
47
+ searchInput="text"
48
+ selectOnBlur={true}
49
+ selectOnNavigation={true}
50
+ wrapSelection={true}
51
+ >
52
+ <DropdownMenu>
53
+ <Connect(RuleImplementationsDownload) />
54
+ <Connect(ImplementationsUploadButton) />
55
+ <Connect(RuleResultsUpload) />
56
+ </DropdownMenu>
57
+ </Dropdown>
58
+ `;
@@ -58,6 +58,5 @@ exports[`<RuleProperties /> matches the latest snapshot 1`] = `
58
58
  foo
59
59
  </ListContent>
60
60
  </ListItem>
61
- <Connect(RuleRangeNumber) />
62
61
  </List>
63
62
  `;
@@ -0,0 +1,18 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<RuleResultsUpload /> matches the latest snapshot 1`] = `
4
+ <UploadModal
5
+ content="uploadModal.actions.upload.confirmation.content"
6
+ handleSubmit={[Function]}
7
+ header="ruleResults.actions.upload.confirmation.header"
8
+ icon="upload"
9
+ param="rule_results"
10
+ trigger={
11
+ <DropdownItem
12
+ disabled={false}
13
+ icon="upload"
14
+ text="ruleResults.actions.upload.tooltip"
15
+ />
16
+ }
17
+ />
18
+ `;
@@ -24,19 +24,6 @@ exports[`<RuleRow /> matches the latest snapshot 1`] = `
24
24
  >
25
25
 
26
26
  </td>
27
- <td
28
- class=""
29
- />
30
- <td
31
- class="right aligned"
32
- >
33
- 1%
34
- </td>
35
- <td
36
- class="right aligned"
37
- >
38
- 22%
39
- </td>
40
27
  </tr>
41
28
  </tbody>
42
29
  </table>
@@ -66,21 +53,6 @@ exports[`<RuleRow /> row prints column fields 1`] = `
66
53
  class=""
67
54
  >
68
55
 
69
- </td>
70
- <td
71
- class=""
72
- >
73
- Percentage
74
- </td>
75
- <td
76
- class="right aligned"
77
- >
78
- 1%
79
- </td>
80
- <td
81
- class="right aligned"
82
- >
83
- 22%
84
56
  </td>
85
57
  <td
86
58
  class=""
@@ -57,47 +57,6 @@ exports[`<Rules /> matches the latest snapshot 1`] = `
57
57
  sorted={null}
58
58
  width={3}
59
59
  />
60
- <TableHeaderCell
61
- as="th"
62
- className=""
63
- content={
64
- <Memo(MemoizedFormattedMessage)
65
- id="quality.result_type"
66
- />
67
- }
68
- key="3"
69
- onClick={[Function]}
70
- sorted={null}
71
- width={2}
72
- />
73
- <TableHeaderCell
74
- as="th"
75
- className=""
76
- content={
77
- <Memo(MemoizedFormattedMessage)
78
- id="quality.minimum"
79
- />
80
- }
81
- key="4"
82
- onClick={[Function]}
83
- sorted={null}
84
- textAlign="right"
85
- width={1}
86
- />
87
- <TableHeaderCell
88
- as="th"
89
- className=""
90
- content={
91
- <Memo(MemoizedFormattedMessage)
92
- id="quality.goal.list"
93
- />
94
- }
95
- key="5"
96
- onClick={[Function]}
97
- sorted={null}
98
- textAlign="right"
99
- width={1}
100
- />
101
60
  </TableRow>
102
61
  </TableHeader>
103
62
  <TableBody
@@ -131,34 +90,6 @@ exports[`<Rules /> matches the latest snapshot 1`] = `
131
90
  },
132
91
  "width": 3,
133
92
  },
134
- Object {
135
- "fieldDecorator": [Function],
136
- "name": "result_type",
137
- "sort": Object {
138
- "name": "result_type.raw",
139
- },
140
- "width": 2,
141
- },
142
- Object {
143
- "fieldDecorator": [Function],
144
- "fieldSelector": [Function],
145
- "name": "minimum",
146
- "sort": Object {
147
- "name": "minimum",
148
- },
149
- "textAlign": "right",
150
- "width": 1,
151
- },
152
- Object {
153
- "fieldDecorator": [Function],
154
- "fieldSelector": [Function],
155
- "name": "goal.list",
156
- "sort": Object {
157
- "name": "goal",
158
- },
159
- "textAlign": "right",
160
- "width": 1,
161
- },
162
93
  ]
163
94
  }
164
95
  key="0"
@@ -166,9 +97,7 @@ exports[`<Rules /> matches the latest snapshot 1`] = `
166
97
  Object {
167
98
  "business_concept_id": "2D2B3",
168
99
  "description": "desc1",
169
- "goal": 10,
170
100
  "id": 123,
171
- "minimum": 1,
172
101
  "name": "control1",
173
102
  "status": "status1",
174
103
  "type": "type1",
@@ -204,34 +133,6 @@ exports[`<Rules /> matches the latest snapshot 1`] = `
204
133
  },
205
134
  "width": 3,
206
135
  },
207
- Object {
208
- "fieldDecorator": [Function],
209
- "name": "result_type",
210
- "sort": Object {
211
- "name": "result_type.raw",
212
- },
213
- "width": 2,
214
- },
215
- Object {
216
- "fieldDecorator": [Function],
217
- "fieldSelector": [Function],
218
- "name": "minimum",
219
- "sort": Object {
220
- "name": "minimum",
221
- },
222
- "textAlign": "right",
223
- "width": 1,
224
- },
225
- Object {
226
- "fieldDecorator": [Function],
227
- "fieldSelector": [Function],
228
- "name": "goal.list",
229
- "sort": Object {
230
- "name": "goal",
231
- },
232
- "textAlign": "right",
233
- "width": 1,
234
- },
235
136
  ]
236
137
  }
237
138
  key="1"
@@ -239,9 +140,7 @@ exports[`<Rules /> matches the latest snapshot 1`] = `
239
140
  Object {
240
141
  "business_concept_id": "2D2B4",
241
142
  "description": "desc2",
242
- "goal": 10,
243
143
  "id": 122,
244
- "minimum": 1,
245
144
  "name": "control2",
246
145
  "status": "status2",
247
146
  "type": "type2",
@@ -110,6 +110,7 @@ const Filter = ({
110
110
  composeValue={composeValue}
111
111
  population={clause?.population || [{}]}
112
112
  onChange={onValueConditionChange}
113
+ onModifierChange={onModifierChange}
113
114
  parentStructures={parentStructures}
114
115
  siblings={findSiblings(clause)}
115
116
  scope="population"
@@ -6,6 +6,8 @@ import { useIntl } from "react-intl";
6
6
  import { Form, Icon, Popup } from "semantic-ui-react";
7
7
  import { selectTemplate } from "@truedat/df/routines";
8
8
 
9
+ import LimitsForm from "./LimitsForm";
10
+
9
11
  const TemplateSelector = React.lazy(() =>
10
12
  import("@truedat/df/templates/components/TemplateSelector")
11
13
  );
@@ -33,12 +35,11 @@ const ImplementationDynamicForm = ({
33
35
  ? firstTemplate
34
36
  : {}
35
37
  );
36
-
37
- selectTemplate({ id: templateId });
38
+ if (templateId !== undefined) selectTemplate({ id: templateId });
38
39
  }, [ruleImplementation, selectTemplate, templates]);
39
40
 
40
41
  const handleContentChange = (content) => onChange("dfContent", content);
41
- const handleTemplateSelected = (e, { value }) =>
42
+ const handleTemplateSelected = (_e, { value }) =>
42
43
  selectTemplate({ id: value });
43
44
 
44
45
  return (
@@ -47,7 +48,6 @@ const ImplementationDynamicForm = ({
47
48
  name="template"
48
49
  selectedValue={_.prop("id")(template)}
49
50
  onChange={handleTemplateSelected}
50
- isOptional={true}
51
51
  />
52
52
  {template && template.id && (
53
53
  <DynamicForm
@@ -113,7 +113,7 @@ export const InformationForm = ({
113
113
  autoComplete="off"
114
114
  />
115
115
  </Form.Field>
116
-
116
+ <LimitsForm onChange={onChange} ruleImplementation={ruleImplementation} />
117
117
  {!_.isEmpty(templates) && (
118
118
  <ImplementationDynamicForm
119
119
  selectTemplate={selectTemplate}
@@ -0,0 +1,142 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { useIntl } from "react-intl";
5
+ import { Form, Icon, Popup, Label, Segment } from "semantic-ui-react";
6
+
7
+ import generateValidationMessages from "./limitsValidation";
8
+
9
+ export const FieldLabelWrapping = ({
10
+ children,
11
+ label,
12
+ required,
13
+ tooltip,
14
+ message,
15
+ }) => (
16
+ <Form.Field>
17
+ <label className="rule-form-label">
18
+ {label}
19
+ {required && <span>*</span>}
20
+ {tooltip && (
21
+ <Popup
22
+ trigger={
23
+ <Icon className="rule-form-popup" name="question circle outline" />
24
+ }
25
+ content={tooltip}
26
+ on="click"
27
+ hideOnScroll
28
+ />
29
+ )}
30
+ {message && <Label pointing="left">{message}</Label>}
31
+ </label>
32
+ {children}
33
+ </Form.Field>
34
+ );
35
+
36
+ FieldLabelWrapping.propTypes = {
37
+ children: PropTypes.node,
38
+ label: PropTypes.string,
39
+ required: PropTypes.bool,
40
+ tooltip: PropTypes.string,
41
+ message: PropTypes.string,
42
+ };
43
+
44
+ export default function LimitsForm({ onChange, ruleImplementation }) {
45
+ const { formatMessage } = useIntl();
46
+ const handleChange = (e, { name, value }) => {
47
+ e && e.preventDefault();
48
+ onChange(name, value);
49
+ };
50
+
51
+ const { minimum: minimumMessage, goal: goalMessage } =
52
+ generateValidationMessages(ruleImplementation);
53
+
54
+ return (
55
+ <Segment>
56
+ <FieldLabelWrapping
57
+ label={formatMessage({ id: "ruleImplementations.props.result_type" })}
58
+ required
59
+ tooltip={formatMessage({
60
+ id: "ruleImplementation.form.tooltip.result_type",
61
+ })}
62
+ >
63
+ <Form.Group inline>
64
+ <Form.Radio
65
+ name={"result_type"}
66
+ label={formatMessage({
67
+ id: "ruleImplementations.props.result_type.percentage",
68
+ })}
69
+ value={"percentage"}
70
+ checked={ruleImplementation.result_type == "percentage"}
71
+ onChange={handleChange}
72
+ />
73
+ <Form.Radio
74
+ name={"result_type"}
75
+ label={formatMessage({
76
+ id: "ruleImplementations.props.result_type.deviation",
77
+ })}
78
+ value={"deviation"}
79
+ checked={ruleImplementation.result_type == "deviation"}
80
+ onChange={handleChange}
81
+ />
82
+ <Form.Radio
83
+ name={"result_type"}
84
+ label={formatMessage({
85
+ id: "ruleImplementations.props.result_type.errors_number",
86
+ })}
87
+ value={"errors_number"}
88
+ checked={ruleImplementation.result_type == "errors_number"}
89
+ onChange={handleChange}
90
+ />
91
+ </Form.Group>
92
+ </FieldLabelWrapping>
93
+ <FieldLabelWrapping
94
+ label={formatMessage({ id: "ruleImplementations.props.minimum" })}
95
+ message={minimumMessage && formatMessage({ id: minimumMessage })}
96
+ required
97
+ tooltip={formatMessage({
98
+ id: `ruleImplementation.form.tooltip.${ruleImplementation.result_type}.minimum`,
99
+ })}
100
+ >
101
+ <Form.Input
102
+ name="minimum"
103
+ onChange={handleChange}
104
+ value={
105
+ _.isNil(ruleImplementation.minimum)
106
+ ? ""
107
+ : ruleImplementation.minimum
108
+ }
109
+ placeholder={formatMessage({
110
+ id: "ruleImplementations.props.minimum.placeholder",
111
+ })}
112
+ autoComplete="off"
113
+ />
114
+ </FieldLabelWrapping>
115
+ <FieldLabelWrapping
116
+ label={formatMessage({ id: "ruleImplementations.props.goal" })}
117
+ message={goalMessage && formatMessage({ id: goalMessage })}
118
+ required
119
+ tooltip={formatMessage({
120
+ id: `ruleImplementation.form.tooltip.${ruleImplementation.result_type}.goal`,
121
+ })}
122
+ >
123
+ <Form.Input
124
+ name="goal"
125
+ onChange={handleChange}
126
+ value={
127
+ _.isNil(ruleImplementation.goal) ? "" : ruleImplementation.goal
128
+ }
129
+ placeholder={formatMessage({
130
+ id: "ruleImplementations.props.goal.placeholder",
131
+ })}
132
+ autoComplete="off"
133
+ />
134
+ </FieldLabelWrapping>
135
+ </Segment>
136
+ );
137
+ }
138
+
139
+ LimitsForm.propTypes = {
140
+ ruleImplementation: PropTypes.object,
141
+ onChange: PropTypes.func,
142
+ };
@@ -5,10 +5,12 @@ import { connect } from "react-redux";
5
5
  import { useHistory } from "react-router-dom";
6
6
  import { FormattedMessage, useIntl } from "react-intl";
7
7
  import { Button, Divider, Form, Grid, Icon, Step } from "semantic-ui-react";
8
+ import { validateContent } from "@truedat/df/utils";
8
9
  import PopulationForm from "./PopulationForm";
9
10
  import DatasetForm from "./DatasetForm";
10
11
  import ValidationsForm from "./ValidationsForm";
11
12
  import InformationForm from "./InformationForm";
13
+ import { areLimitsValid } from "./limitsValidation";
12
14
 
13
15
  const RuleImplementationStep = ({ activeStep, name, icon }) => (
14
16
  <Step active={activeStep == name}>
@@ -37,12 +39,12 @@ export const RuleImplementationForm = ({
37
39
  handleSubmit,
38
40
  operators,
39
41
  onChange,
40
- applyTemplate,
42
+ template,
41
43
  }) => {
42
44
  const { formatMessage } = useIntl();
43
45
  const history = useHistory();
44
46
  const steps = [
45
- { name: "information", icon: "info", isValid: () => true },
47
+ { name: "information", icon: "info", isValid: () => validInformation() },
46
48
  { name: "dataset", icon: "database", isValid: () => validDataSet() },
47
49
  { name: "population", icon: "user", isValid: () => validPopulation() },
48
50
  { name: "validations", icon: "setting", isValid: () => validValidations() },
@@ -55,8 +57,15 @@ export const RuleImplementationForm = ({
55
57
  dataset: structures,
56
58
  population,
57
59
  validations,
60
+ dfContent,
58
61
  } = ruleImplementation || {};
59
62
 
63
+ const validInformation = () =>
64
+ template &&
65
+ !_.isEmpty(template) &&
66
+ _.size(validateContent(template)(dfContent)) == 0 &&
67
+ areLimitsValid(ruleImplementation);
68
+
60
69
  const validPopulation = () =>
61
70
  _.every(_.isEmpty)(population) || populationComplete();
62
71
 
@@ -208,7 +217,6 @@ export const RuleImplementationForm = ({
208
217
  setImplementationKey,
209
218
  ruleImplementation,
210
219
  onChange,
211
- applyTemplate,
212
220
  }}
213
221
  />
214
222
  )}
@@ -241,6 +249,7 @@ export const RuleImplementationForm = ({
241
249
  )}
242
250
  </Form>
243
251
  </Grid.Row>
252
+ <Divider hidden />
244
253
  <Grid.Row stretched>
245
254
  <Button
246
255
  floated="right"
@@ -287,11 +296,12 @@ RuleImplementationForm.propTypes = {
287
296
  setValidations: PropTypes.func,
288
297
  operators: PropTypes.object,
289
298
  onChange: PropTypes.func,
290
- applyTemplate: PropTypes.func,
299
+ template: PropTypes.object,
291
300
  };
292
301
 
293
302
  const mapStateToProps = (state) => ({
294
303
  isSubmitting: state.ruleImplementationCreating,
304
+ template: state.template,
295
305
  });
296
306
 
297
307
  export default connect(mapStateToProps)(RuleImplementationForm);
@@ -5,7 +5,10 @@ import { connect } from "react-redux";
5
5
  import { useHistory } from "react-router-dom";
6
6
  import { FormattedMessage, useIntl } from "react-intl";
7
7
  import { Button, Form, Icon, Popup } from "semantic-ui-react";
8
+ import { validateContent as validDfContent } from "@truedat/df/utils";
8
9
  import { selectTemplate } from "@truedat/df/routines";
10
+ import LimitsForm from "./LimitsForm";
11
+ import { areLimitsValid } from "./limitsValidation";
9
12
 
10
13
  const Help = ({ message }) => {
11
14
  const { formatMessage } = useIntl();
@@ -60,7 +63,6 @@ const ImplementationDynamicForm = ({
60
63
  name="template"
61
64
  selectedValue={_.prop("id")(template)}
62
65
  onChange={handleTemplateSelected}
63
- isOptional={true}
64
66
  />
65
67
  {template && template.id && (
66
68
  <DynamicForm
@@ -84,7 +86,6 @@ export const RuleImplementationRawForm = ({
84
86
  handleSubmit,
85
87
  isSubmitting,
86
88
  implementationKey,
87
- mode,
88
89
  rawContent,
89
90
  setImplementationKey,
90
91
  setImplementationRawContent,
@@ -148,9 +149,15 @@ export const RuleImplementationRawForm = ({
148
149
  /\b(DROP|DELETE|INSERT|UPDATE|CALL|EXEC|EXECUTE|ALTER)\b|;|--|#/gi;
149
150
  return text ? text.match(re) : [];
150
151
  };
152
+ const validInformation = () =>
153
+ template &&
154
+ !_.isEmpty(template) &&
155
+ _.size(validDfContent(template)(ruleImplementation?.dfContent)) == 0 &&
156
+ areLimitsValid(ruleImplementation);
151
157
 
152
158
  const isValidForm = () => {
153
159
  return (
160
+ validInformation() &&
154
161
  isNotEmptyProp("source_id") &&
155
162
  (_.isEmpty(databasesOptions) || isNotEmptyProp("database")) &&
156
163
  _.every(isNotEmptyProp)(["dataset", "validations"]) &&
@@ -208,11 +215,14 @@ export const RuleImplementationRawForm = ({
208
215
  placeholder={formatMessage({
209
216
  id: "ruleImplementation.props.name.placeholder",
210
217
  })}
211
- readOnly={mode == "edition"}
212
218
  required
213
219
  value={implementationKey}
214
220
  />
215
221
  </Form.Field>
222
+ <LimitsForm
223
+ onChange={onChange}
224
+ ruleImplementation={ruleImplementation}
225
+ />
216
226
  {!_.isEmpty(templates) && (
217
227
  <ImplementationDynamicForm
218
228
  selectTemplate={selectTemplate}
@@ -279,7 +289,7 @@ export const RuleImplementationRawForm = ({
279
289
  required
280
290
  rows={4}
281
291
  size="small"
282
- value={_.prop("dataset")(rawContent)}
292
+ value={_.prop("dataset")(rawContent) || ""}
283
293
  />
284
294
  <Form.TextArea
285
295
  className="raw"
@@ -303,7 +313,7 @@ export const RuleImplementationRawForm = ({
303
313
  })}
304
314
  rows={4}
305
315
  size="small"
306
- value={_.prop("population")(rawContent)}
316
+ value={_.prop("population")(rawContent) || ""}
307
317
  />
308
318
  <Form.TextArea
309
319
  className="raw"
@@ -328,7 +338,7 @@ export const RuleImplementationRawForm = ({
328
338
  required
329
339
  rows={4}
330
340
  size="small"
331
- value={_.prop("validations")(rawContent)}
341
+ value={_.prop("validations")(rawContent) || ""}
332
342
  />
333
343
  <Form.Button
334
344
  floated="right"
@@ -64,6 +64,16 @@ export const ValueConditions = ({
64
64
 
65
65
  const onRowAddition = () => onChange(_.concat(population)([{}]));
66
66
 
67
+ const onModifierChange = (index, modifier) => {
68
+ const clause = _.nth(index)(population);
69
+ const updatedCondition = {
70
+ ...clause,
71
+ modifier,
72
+ };
73
+ const modifiedPopulation = replaceAt(index, updatedCondition)(population);
74
+ onChange && onChange(modifiedPopulation);
75
+ };
76
+
67
77
  return population.map((clause, index) => (
68
78
  <React.Fragment key={index}>
69
79
  <Grid.Row>
@@ -74,6 +84,7 @@ export const ValueConditions = ({
74
84
  onOperatorChange={onOperatorChange}
75
85
  onStructureChange={onStructureChange}
76
86
  onValueChange={onValueChange}
87
+ onModifierChange={onModifierChange}
77
88
  parentStructures={parentStructures}
78
89
  scope={scope}
79
90
  siblings={siblings}