@truedat/dq 4.49.5 → 4.49.8

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.
@@ -1,5 +1,5 @@
1
1
  import _ from "lodash/fp";
2
- import React, { useState } from "react";
2
+ import React, { useEffect, useState } from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import { connect } from "react-redux";
5
5
  import { useHistory } from "react-router-dom";
@@ -13,6 +13,7 @@ import {
13
13
  Step,
14
14
  Segment,
15
15
  } from "semantic-ui-react";
16
+ import { Loading } from "@truedat/core/components";
16
17
  import PopulationForm from "./PopulationForm";
17
18
  import DatasetForm from "./DatasetForm";
18
19
  import ValidationsForm from "./ValidationsForm";
@@ -20,8 +21,13 @@ import InformationForm from "./InformationForm";
20
21
  import SegmentsForm from "./SegmentsForm";
21
22
  import { areLimitsValid } from "./limitsValidation";
22
23
 
23
- const RuleImplementationStep = ({ activeStep, name, icon }) => (
24
- <Step active={activeStep == name}>
24
+ const RuleImplementationStep = ({
25
+ activeStep,
26
+ name,
27
+ icon,
28
+ disabled = false,
29
+ }) => (
30
+ <Step active={activeStep == name} disabled={disabled}>
25
31
  <Icon {...icon} />
26
32
  <Step.Content>
27
33
  <Step.Title>
@@ -38,15 +44,12 @@ RuleImplementationStep.propTypes = {
38
44
  };
39
45
 
40
46
  export const RuleImplementationForm = ({
41
- actions,
42
47
  addPopulation,
43
48
  addSegments,
44
- authManageSegments,
45
49
  isSubmitting,
46
50
  onChange,
47
51
  onSubmit,
48
52
  operators,
49
- rule,
50
53
  ruleImplementation,
51
54
  setImplementationKey,
52
55
  setPopulations,
@@ -56,63 +59,131 @@ export const RuleImplementationForm = ({
56
59
  }) => {
57
60
  const { formatMessage } = useIntl();
58
61
  const history = useHistory();
59
- const steps = [
60
- { name: "information", icon: "info", isValid: () => validInformation() },
61
- { name: "dataset", icon: "database", isValid: () => validDataSet() },
62
- { name: "populations", icon: "user", isValid: () => validPopulations() },
63
- { name: "validations", icon: "setting", isValid: () => validValidations() },
64
- ...(authManageSegments
65
- ? [
66
- {
67
- name: "segments",
68
- icon: "grid layout",
69
- isValid: () => validSegments(),
70
- },
71
- ]
72
- : []),
73
- ];
74
62
 
63
+ const [steps, setSteps] = useState([]);
75
64
  const [activeStep, setActiveStep] = useState("information");
76
65
  const [selector, setSelector] = useState();
77
- const [isContentValid, setContentIsValid] = useState();
78
-
79
- const {
80
- dataset: structures,
81
- populations,
82
- validations,
83
- dfName,
84
- segments,
85
- } = ruleImplementation || {};
86
-
87
- const validInformation = () =>
88
- dfName &&
89
- !_.isEmpty(dfName) &&
90
- isContentValid &&
91
- (!_.isEmpty(rule) || _.isNumber(_.prop("domain_id")(ruleImplementation))) &&
92
- areLimitsValid(ruleImplementation);
93
-
94
- const validPopulations = () =>
66
+ const [isContentValid, setContentIsValid] = useState(false);
67
+ const [domains, setDomains] = useState();
68
+ const [canPublishCurrentDomain, setCanPublishCurrentDomain] = useState(false);
69
+ const [canManageSegmentsCurrentDomain, setCanManageSegmentsCurrentDomain] =
70
+ useState(false);
71
+ const [canManageSegmentsAnyDomain, setCanManageSegmentsAnyDomain] =
72
+ useState(false);
73
+
74
+ const [isLoading, setIsLoading] = useState(true);
75
+ const [isLoadingDomains, setIsLoadingDomains] = useState(true);
76
+ const [isLoadingActions, setIsLoadingActions] = useState(true);
77
+
78
+ useEffect(() => {
79
+ if (!isLoadingDomains) {
80
+ _.flow(
81
+ _.reduce((actions, domain) => _.concat(actions, domain.actions), []),
82
+ _.any((action) => action == "manageSegments"),
83
+ setCanManageSegmentsAnyDomain
84
+ )(domains);
85
+
86
+ const currentDomainActions = _.flow(
87
+ _.find((domain) => domain.id === String(ruleImplementation.domain_id)),
88
+ _.pathOr([], "actions")
89
+ )(domains);
90
+
91
+ _.flow(
92
+ _.any((action) => action == "manageSegments"),
93
+ setCanManageSegmentsCurrentDomain
94
+ )(currentDomainActions);
95
+
96
+ _.flow(
97
+ _.any((action) => action == "publishImplementation"),
98
+ setCanPublishCurrentDomain
99
+ )(currentDomainActions);
100
+
101
+ setIsLoadingActions(false);
102
+ }
103
+ return () => {
104
+ setCanPublishCurrentDomain(false);
105
+ setCanManageSegmentsCurrentDomain(false);
106
+ setCanManageSegmentsAnyDomain(false);
107
+ };
108
+ }, [domains, ruleImplementation.domain_id, isLoadingDomains]);
109
+
110
+ useEffect(() => {
111
+ const {
112
+ dataset: structures,
113
+ populations,
114
+ validations,
115
+ dfName,
116
+ segments,
117
+ } = ruleImplementation || {};
118
+
119
+ const validInformation = () =>
120
+ !_.isEmpty(dfName) &&
121
+ isContentValid &&
122
+ !!ruleImplementation?.domain_id &&
123
+ areLimitsValid(ruleImplementation);
124
+
125
+ const validValidations = () =>
126
+ _.every(_.negate(_.isEmpty))(validations) &&
127
+ validationsComplete(validations);
128
+
129
+ const validSegments = () => _.every(_.negate(_.isEmpty))(segments);
130
+
131
+ const validDataSet = () => areStructuresComplete();
132
+
133
+ const areStructuresComplete = () =>
134
+ !_.isEmpty(structures) &&
135
+ _.every(
136
+ (i) =>
137
+ itemComplete(_.nth(i)(structures), [
138
+ "structure.id",
139
+ "structure.name",
140
+ ]) &&
141
+ (i == 0 ||
142
+ _.flow(_.nth(i), _.prop("clauses"), areClausesComplete)(structures))
143
+ )(_.range(0, _.size(structures)));
144
+
145
+ if (!isLoadingActions) {
146
+ setSteps([
147
+ {
148
+ name: "information",
149
+ icon: "info",
150
+ isValid: () => validInformation(),
151
+ },
152
+ { name: "dataset", icon: "database", isValid: () => validDataSet() },
153
+ {
154
+ name: "populations",
155
+ icon: "user",
156
+ isValid: () => validPopulations(populations),
157
+ },
158
+ {
159
+ name: "validations",
160
+ icon: "setting",
161
+ isValid: () => validValidations(),
162
+ },
163
+ ...(canManageSegmentsAnyDomain
164
+ ? [
165
+ {
166
+ name: "segments",
167
+ icon: "grid layout",
168
+ isValid: () => validSegments(),
169
+ disabled: !canManageSegmentsCurrentDomain,
170
+ },
171
+ ]
172
+ : []),
173
+ ]);
174
+ setIsLoading(false);
175
+ }
176
+ }, [
177
+ canManageSegmentsAnyDomain,
178
+ canManageSegmentsCurrentDomain,
179
+ ruleImplementation,
180
+ isContentValid,
181
+ isLoadingActions,
182
+ ]);
183
+
184
+ const validPopulations = (populations) =>
95
185
  _.every((population) => populationComplete(population))(populations);
96
186
 
97
- const validValidations = () =>
98
- _.every(_.negate(_.isEmpty))(validations) && validationsComplete();
99
-
100
- const validSegments = () => _.every(_.negate(_.isEmpty))(segments);
101
-
102
- const validDataSet = () => areStructuresComplete();
103
-
104
- const areStructuresComplete = () =>
105
- !_.isEmpty(structures) &&
106
- _.every(
107
- (i) =>
108
- itemComplete(_.nth(i)(structures), [
109
- "structure.id",
110
- "structure.name",
111
- ]) &&
112
- (i == 0 ||
113
- _.flow(_.nth(i), _.prop("clauses"), areClausesComplete)(structures))
114
- )(_.range(0, _.size(structures)));
115
-
116
187
  const areClausesComplete = (clauses) =>
117
188
  _.size(clauses) > 0 &&
118
189
  _.every((c) => itemComplete(c, ["left", "right"]))(clauses);
@@ -121,7 +192,7 @@ export const RuleImplementationForm = ({
121
192
  _.every(_.negate(_.isEmpty))(population) &&
122
193
  _.every((p) => itemComplete(p, attrsFromProfile(p)))(population);
123
194
 
124
- const validationsComplete = () =>
195
+ const validationsComplete = (validations) =>
125
196
  _.every((v) => itemComplete(v, attrsFromProfile(v)))(validations);
126
197
 
127
198
  const attrsFromProfile = (p) => {
@@ -174,11 +245,16 @@ export const RuleImplementationForm = ({
174
245
  const currentStepIndex = _.findIndex((step) => step.name == stepName)(
175
246
  steps
176
247
  );
177
- if (currentStepIndex + 1 >= steps.length) {
178
- return null;
179
- } else {
180
- return _.prop("name")(_.nth(currentStepIndex + 1)(steps));
181
- }
248
+ const nextStep =
249
+ currentStepIndex + 1 >= steps.length
250
+ ? null
251
+ : _.nth(currentStepIndex + 1)(steps);
252
+
253
+ return _.isEmpty(nextStep)
254
+ ? null
255
+ : nextStep?.disabled
256
+ ? getNextStep(nextStep.name)
257
+ : _.prop("name")(nextStep);
182
258
  };
183
259
 
184
260
  const setPreviousStep = (stepName) => {
@@ -190,17 +266,25 @@ export const RuleImplementationForm = ({
190
266
  const currentStepIndex = _.findIndex((step) => step.name == stepName)(
191
267
  steps
192
268
  );
193
- if (currentStepIndex - 1 < 0) {
194
- return null;
195
- } else {
196
- return _.prop("name")(_.nth(currentStepIndex - 1)(steps));
197
- }
269
+
270
+ const prevStep =
271
+ currentStepIndex - 1 < 0 ? null : _.nth(currentStepIndex - 1)(steps);
272
+
273
+ return _.isEmpty(prevStep)
274
+ ? null
275
+ : prevStep?.disabled
276
+ ? getPreviousStep(prevStep.name)
277
+ : _.prop("name")(prevStep);
198
278
  };
199
279
 
200
280
  const isButtonDisabled = (currentStep) => !validStep(currentStep);
201
281
 
202
- const validStep = (stepName) =>
203
- _.prop("isValid")(_.find((step) => step.name == stepName)(steps))();
282
+ const validStep = (stepName) => {
283
+ return _.flow(
284
+ _.find((step) => step.name == stepName),
285
+ _.pathOr(() => false, "isValid")
286
+ )(steps)();
287
+ };
204
288
 
205
289
  const completeStep = (index, activeStep) => {
206
290
  if (index >= _.findIndex((s) => s.name == activeStep)(steps)) return false;
@@ -215,155 +299,171 @@ export const RuleImplementationForm = ({
215
299
 
216
300
  const doCancel = () => history.goBack();
217
301
 
302
+ const {
303
+ dataset: structures,
304
+ populations,
305
+ validations,
306
+ segments,
307
+ } = ruleImplementation || {};
218
308
  return (
219
- <Grid.Column>
220
- <Grid.Row stretched textAlign="center">
221
- <Step.Group size="small">
222
- {steps.map((step, index) => (
223
- <RuleImplementationStep
224
- key={index}
225
- name={step.name}
226
- icon={
227
- completeStep(index, activeStep)
228
- ? { name: "check", color: "green" }
229
- : { name: step.icon }
230
- }
231
- activeStep={activeStep}
232
- />
233
- ))}
234
- </Step.Group>
235
- <Divider fitted hidden />
236
- </Grid.Row>
237
- <Grid.Row stretched className="rule-impl-form-row">
238
- <Form>
239
- {activeStep == "information" && (
240
- <InformationForm
241
- {...{
242
- onChange,
243
- ruleImplementation,
244
- setImplementationKey,
245
- setIsValid: setContentIsValid,
246
- }}
247
- />
248
- )}
249
- {activeStep == "dataset" && (
250
- <DatasetForm
251
- {...{
252
- setImplementationKey,
253
- structures,
254
- selector,
255
- setStructures,
256
- setSelector,
257
- }}
258
- />
259
- )}
260
- {activeStep == "populations" ? (
261
- <>
262
- {populations.map((population, i) => (
263
- <Segment key={i}>
264
- <PopulationForm
265
- population={population}
266
- setPopulation={(p) => setPopulations(p, i)}
309
+ <>
310
+ {isLoading ? <Loading /> : null}
311
+ <Grid.Column style={{ visibility: isLoading ? "hidden" : "visible" }}>
312
+ <Grid.Row stretched textAlign="center">
313
+ <Step.Group size="small">
314
+ {steps.map((step, index) => (
315
+ <RuleImplementationStep
316
+ key={index}
317
+ disabled={step.disabled}
318
+ name={step.name}
319
+ icon={
320
+ completeStep(index, activeStep) && !step.disabled
321
+ ? { name: "check", color: "green" }
322
+ : {
323
+ name: step.icon,
324
+ color: step.disabled ? "grey" : "black",
325
+ }
326
+ }
327
+ activeStep={activeStep}
328
+ />
329
+ ))}
330
+ </Step.Group>
331
+ <Divider fitted hidden />
332
+ </Grid.Row>
333
+ <Grid.Row stretched className="rule-impl-form-row">
334
+ <Form>
335
+ {activeStep == "information" && (
336
+ <InformationForm
337
+ {...{
338
+ onChange,
339
+ ruleImplementation,
340
+ setImplementationKey,
341
+ setIsValid: setContentIsValid,
342
+ onDomainsLoad: (domains) => {
343
+ setDomains(domains);
344
+ setIsLoadingDomains(false);
345
+ },
346
+ }}
347
+ />
348
+ )}
349
+ {activeStep == "dataset" && (
350
+ <DatasetForm
351
+ {...{
352
+ setImplementationKey,
353
+ structures,
354
+ selector,
355
+ setStructures,
356
+ setSelector,
357
+ }}
358
+ />
359
+ )}
360
+ {activeStep == "populations" ? (
361
+ <>
362
+ {populations.map((population, i) => (
363
+ <Segment key={i}>
364
+ <PopulationForm
365
+ population={population}
366
+ setPopulation={(p) => setPopulations(p, i)}
367
+ structures={structures}
368
+ typeOperators={operators}
369
+ />
370
+ </Segment>
371
+ ))}
372
+ {_.flow(_.flatten, _.isEmpty)(populations) ? (
373
+ <PopulationTypeForm createFilter={() => addPopulation()} />
374
+ ) : validPopulations(populations) ? (
375
+ <Button
376
+ secondary
377
+ onClick={() => addPopulation()}
378
+ content={formatMessage({
379
+ id: "ruleImplementationForm.populations.add",
380
+ })}
381
+ />
382
+ ) : null}
383
+ </>
384
+ ) : null}
385
+ {activeStep == "validations" && (
386
+ <ValidationsForm
387
+ validations={validations}
388
+ setValidations={setValidations}
389
+ structures={structures}
390
+ typeOperators={operators}
391
+ />
392
+ )}
393
+ {activeStep == "segments" ? (
394
+ <>
395
+ {_.flow(_.flatten, _.isEmpty)(segments) ? (
396
+ <SegmentsTypeForm createFilter={() => addSegments()} />
397
+ ) : (
398
+ <SegmentsForm
399
+ segments={segments}
400
+ setSegments={setSegments}
267
401
  structures={structures}
268
402
  typeOperators={operators}
269
403
  />
270
- </Segment>
271
- ))}
272
- {_.flow(_.flatten, _.isEmpty)(populations) ? (
273
- <PopulationTypeForm createFilter={() => addPopulation()} />
274
- ) : validPopulations() ? (
404
+ )}
405
+ </>
406
+ ) : null}
407
+ </Form>
408
+ </Grid.Row>
409
+ <Divider hidden />
410
+ <Grid.Row stretched>
411
+ {getNextStep(activeStep) == null ? (
412
+ <>
413
+ {canPublishCurrentDomain ? (
275
414
  <Button
276
- secondary
277
- onClick={() => addPopulation()}
278
- content={formatMessage({
279
- id: "ruleImplementationForm.populations.add",
280
- })}
415
+ floated="right"
416
+ disabled={isButtonDisabled(activeStep)}
417
+ type="submit"
418
+ primary
419
+ loading={isSubmitting}
420
+ onClick={() => doSubmit({ status: "published" })}
421
+ content={formatMessage({ id: "actions.publish" })}
281
422
  />
282
423
  ) : null}
283
- </>
284
- ) : null}
285
- {activeStep == "validations" && (
286
- <ValidationsForm
287
- validations={validations}
288
- setValidations={setValidations}
289
- structures={structures}
290
- typeOperators={operators}
291
- />
292
- )}
293
- {activeStep == "segments" ? (
294
- <>
295
- {_.flow(_.flatten, _.isEmpty)(segments) ? (
296
- <SegmentsTypeForm createFilter={() => addSegments()} />
297
- ) : (
298
- <SegmentsForm
299
- segments={segments}
300
- setSegments={setSegments}
301
- structures={structures}
302
- typeOperators={operators}
303
- />
304
- )}
305
- </>
306
- ) : null}
307
- </Form>
308
- </Grid.Row>
309
- <Divider hidden />
310
- <Grid.Row stretched>
311
- {getNextStep(activeStep) == null ? (
312
- <>
313
- {!!actions?.publish ? (
314
424
  <Button
315
425
  floated="right"
316
426
  disabled={isButtonDisabled(activeStep)}
317
427
  type="submit"
318
428
  primary
319
429
  loading={isSubmitting}
320
- onClick={() => doSubmit({ status: "published" })}
321
- content={formatMessage({ id: "actions.publish" })}
430
+ onClick={() => doSubmit({ status: "draft" })}
431
+ content={formatMessage({ id: "actions.save" })}
322
432
  />
323
- ) : null}
433
+ </>
434
+ ) : (
324
435
  <Button
325
436
  floated="right"
326
437
  disabled={isButtonDisabled(activeStep)}
327
438
  type="submit"
328
439
  primary
329
440
  loading={isSubmitting}
330
- onClick={() => doSubmit({})}
331
- content={formatMessage({ id: "actions.save" })}
441
+ onClick={() => setNextStep(activeStep)}
442
+ content={formatMessage({ id: "actions.next" })}
332
443
  />
333
- </>
334
- ) : (
444
+ )}
335
445
  <Button
336
446
  floated="right"
337
- disabled={isButtonDisabled(activeStep)}
338
- type="submit"
339
- primary
340
- loading={isSubmitting}
341
- onClick={() => setNextStep(activeStep)}
342
- content={formatMessage({ id: "actions.next" })}
343
- />
344
- )}
345
- <Button
346
- floated="right"
347
- secondary
348
- onClick={
349
- () =>
447
+ secondary
448
+ onClick={
449
+ () =>
450
+ getPreviousStep(activeStep)
451
+ ? setPreviousStep(activeStep)
452
+ : doCancel() //TODO: confirmModal
453
+ }
454
+ content={
350
455
  getPreviousStep(activeStep)
351
- ? setPreviousStep(activeStep)
352
- : doCancel() //TODO: confirmModal
353
- }
354
- content={
355
- getPreviousStep(activeStep)
356
- ? formatMessage({ id: "actions.prev" })
357
- : formatMessage({ id: "actions.cancel" })
358
- }
359
- />
360
- </Grid.Row>
361
- </Grid.Column>
456
+ ? formatMessage({ id: "actions.prev" })
457
+ : formatMessage({ id: "actions.cancel" })
458
+ }
459
+ />
460
+ </Grid.Row>
461
+ </Grid.Column>
462
+ </>
362
463
  );
363
464
  };
364
465
 
365
466
  RuleImplementationForm.propTypes = {
366
- actions: PropTypes.object,
367
467
  addPopulation: PropTypes.func,
368
468
  addSegments: PropTypes.func,
369
469
  authManageSegments: PropTypes.bool,
@@ -378,22 +478,13 @@ RuleImplementationForm.propTypes = {
378
478
  setSegments: PropTypes.func,
379
479
  setStructures: PropTypes.func,
380
480
  setValidations: PropTypes.func,
381
- rule: PropTypes.object,
382
481
  };
383
482
 
384
- const mapStateToProps = ({
385
- rule,
386
- ruleImplementationCreating,
387
- userRulePermissions,
388
- implementationActions,
389
- }) => ({
390
- actions: implementationActions,
391
- authManageSegments:
392
- !!userRulePermissions?.manage_segments ||
393
- !!implementationActions?.manage_segments,
394
- rule,
395
- isSubmitting: ruleImplementationCreating,
396
- });
483
+ const mapStateToProps = ({ ruleImplementationCreating }) => {
484
+ return {
485
+ isSubmitting: ruleImplementationCreating,
486
+ };
487
+ };
397
488
 
398
489
  export default connect(mapStateToProps)(RuleImplementationForm);
399
490