@truedat/df 8.13.5 → 8.13.7

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 (39) hide show
  1. package/package.json +4 -4
  2. package/src/components/DynamicForm.js +3 -0
  3. package/src/components/DynamicFormWithTranslations.js +3 -0
  4. package/src/components/FieldGroupSegment.js +3 -0
  5. package/src/components/FieldGroupWithTranslations.js +13 -0
  6. package/src/components/SelectDynamicFormWithTranslations.js +7 -1
  7. package/src/components/SelectableDynamicForm.js +7 -1
  8. package/src/components/__tests__/__snapshots__/SelectableDynamicForm.spec.js.snap +18 -1
  9. package/src/components/__tests__/originalContentFlow.spec.js +131 -0
  10. package/src/components/widgets/DateField.js +18 -9
  11. package/src/components/widgets/DateTimeField.js +26 -9
  12. package/src/components/widgets/DynamicField.js +54 -1
  13. package/src/components/widgets/DynamicTableField.js +41 -7
  14. package/src/components/widgets/FieldByWidget.js +36 -6
  15. package/src/components/widgets/__tests__/dateBoundsByWidget.spec.js +133 -0
  16. package/src/components/widgets/__tests__/dateBoundsEndToEnd.spec.js +83 -0
  17. package/src/components/widgets/__tests__/datePickerBoundaries.spec.js +124 -0
  18. package/src/components/widgets/__tests__/dateValueValidation.spec.js +215 -0
  19. package/src/components/widgets/__tests__/existingDateRestrictions.spec.js +354 -0
  20. package/src/templates/components/templateForm/DateRestrictionsForm.js +217 -0
  21. package/src/templates/components/templateForm/DefaultValue.js +79 -11
  22. package/src/templates/components/templateForm/FieldDefinition.js +15 -1
  23. package/src/templates/components/templateForm/TemplateForm.js +5 -0
  24. package/src/templates/components/templateForm/ValuesField.js +7 -0
  25. package/src/templates/components/templateForm/__tests__/FieldDefinition.spec.js +48 -0
  26. package/src/templates/components/templateForm/__tests__/TemplateForm.spec.js +107 -0
  27. package/src/templates/components/templateForm/__tests__/dateRestrictionsForm.spec.js +429 -0
  28. package/src/templates/components/templateForm/__tests__/dateRestrictionsValidation.spec.js +166 -0
  29. package/src/templates/components/templateForm/__tests__/defaultValueBounds.spec.js +191 -0
  30. package/src/templates/components/templateForm/__tests__/defaultValueRestrictions.spec.js +175 -0
  31. package/src/templates/components/templateForm/contentValidation.js +184 -0
  32. package/src/templates/utils/__tests__/dateRestrictions.spec.js +578 -0
  33. package/src/templates/utils/__tests__/originalTableRows.spec.js +129 -0
  34. package/src/templates/utils/__tests__/validateContentDateRestrictions.spec.js +519 -0
  35. package/src/templates/utils/applyTemplate.js +27 -20
  36. package/src/templates/utils/dateRestrictions.js +497 -0
  37. package/src/templates/utils/dateValueUnchanged.js +12 -0
  38. package/src/templates/utils/originalTableRows.js +41 -0
  39. package/src/templates/utils/validateContent.js +75 -4
@@ -1,7 +1,183 @@
1
1
  import _ from "lodash/fp";
2
+ import {
3
+ resolveRestrictions,
4
+ checkValuesAgainstBounds,
5
+ } from "../../utils/dateRestrictions";
2
6
 
3
7
  const arrayAnd = (list) => _.indexOf(true)(list) >= 0;
4
8
 
9
+ const isDateField = (field) =>
10
+ field.type === "date" || field.type === "datetime";
11
+
12
+ const RESTRICTION_UNITS = ["days", "weeks", "months", "years"];
13
+ const RESTRICTION_ERRORS = {
14
+ required: "template.field.restrictions.validation.required",
15
+ invalidDate: "template.field.restrictions.validation.invalid_date",
16
+ invalidAmount: "template.field.restrictions.validation.invalid_amount",
17
+ invalidUnit: "template.field.restrictions.validation.invalid_unit",
18
+ invalidOrder: "template.field.restrictions.validation.invalid_order",
19
+ invalidMode: "template.field.restrictions.validation.invalid_mode",
20
+ };
21
+
22
+ const isInteger = (value) =>
23
+ Number.isInteger(value) ||
24
+ (typeof value === "string" && /^-?\d+$/.test(value.trim()));
25
+
26
+ const isValidCalendarDate = (value, allowDateTime) => {
27
+ if (typeof value !== "string" || !value) return false;
28
+ const match =
29
+ /^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?(Z|[+-]\d{2}:?\d{2})?)?$/.exec(
30
+ value,
31
+ );
32
+ if (!match || (!allowDateTime && match[4] !== undefined)) return false;
33
+
34
+ const [, rawYear, rawMonth, rawDay, rawHour, rawMinute, rawSecond] = match;
35
+ const year = Number(rawYear);
36
+ const month = Number(rawMonth);
37
+ const day = Number(rawDay);
38
+ const hour = rawHour === undefined ? 0 : Number(rawHour);
39
+ const minute = rawMinute === undefined ? 0 : Number(rawMinute);
40
+ const second = rawSecond === undefined ? 0 : Number(rawSecond);
41
+ const offset = match[7];
42
+ const offsetMatch = offset && /^[+-](\d{2}):?(\d{2})$/.exec(offset);
43
+ const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
44
+ const daysInMonth = [
45
+ 31,
46
+ leap ? 29 : 28,
47
+ 31,
48
+ 30,
49
+ 31,
50
+ 30,
51
+ 31,
52
+ 31,
53
+ 30,
54
+ 31,
55
+ 30,
56
+ 31,
57
+ ];
58
+
59
+ return (
60
+ month >= 1 &&
61
+ month <= 12 &&
62
+ day >= 1 &&
63
+ day <= daysInMonth[month - 1] &&
64
+ hour <= 23 &&
65
+ minute <= 59 &&
66
+ second <= 59 &&
67
+ (!offsetMatch ||
68
+ (Number(offsetMatch[1]) <= 23 && Number(offsetMatch[2]) <= 59))
69
+ );
70
+ };
71
+
72
+ const validateStaticSide = (value, fieldType) =>
73
+ typeof value !== "string" || !value.trim()
74
+ ? RESTRICTION_ERRORS.required
75
+ : isValidCalendarDate(value, fieldType === "datetime")
76
+ ? null
77
+ : RESTRICTION_ERRORS.invalidDate;
78
+
79
+ const validateDynamicSide = (side) => ({
80
+ amount:
81
+ !side || !isInteger(side.amount) ? RESTRICTION_ERRORS.invalidAmount : null,
82
+ unit:
83
+ !side || !RESTRICTION_UNITS.includes(side.unit)
84
+ ? RESTRICTION_ERRORS.invalidUnit
85
+ : null,
86
+ });
87
+
88
+ const betweenOrderIsInvalid = (between, fieldType) => {
89
+ const { min, max } = resolveRestrictions({ between }, fieldType);
90
+ return Boolean(min && max && min > max);
91
+ };
92
+
93
+ export const validateDateRestrictions = (field) => {
94
+ if (!isDateField(field) || _.isEmpty(field.restrictions)) return {};
95
+
96
+ const restrictions = field.restrictions;
97
+ const between = restrictions.between;
98
+ const legacyBetween =
99
+ !between &&
100
+ (restrictions.between_start !== undefined ||
101
+ restrictions.between_end !== undefined);
102
+
103
+ const restrictionErrors = ["min_date", "max_date"].flatMap((key) => {
104
+ const side = restrictions[key];
105
+ if (!side) return [];
106
+
107
+ if (side.mode === "dynamic") {
108
+ const sideErrors = validateDynamicSide(side);
109
+ return [
110
+ [`${key}.amount`, sideErrors.amount],
111
+ [`${key}.unit`, sideErrors.unit],
112
+ ];
113
+ }
114
+
115
+ return side.mode === "static"
116
+ ? [[`${key}.value`, validateStaticSide(side.value, field.type)]]
117
+ : [["mode", RESTRICTION_ERRORS.invalidMode]];
118
+ });
119
+
120
+ const betweenErrors =
121
+ between?.mode === "dynamic"
122
+ ? (() => {
123
+ const fromErrors = validateDynamicSide(between.from);
124
+ const toErrors = validateDynamicSide(between.to);
125
+ const hasInvalidSides =
126
+ Object.values(fromErrors).some(Boolean) ||
127
+ Object.values(toErrors).some(Boolean);
128
+ const orderError =
129
+ !hasInvalidSides && betweenOrderIsInvalid(between, field.type)
130
+ ? RESTRICTION_ERRORS.invalidOrder
131
+ : null;
132
+
133
+ return [
134
+ ["between.from.amount", fromErrors.amount],
135
+ ["between.from.unit", fromErrors.unit],
136
+ ["between.to.amount", toErrors.amount],
137
+ ["between.to.unit", toErrors.unit],
138
+ ["between.to.amount", orderError],
139
+ ];
140
+ })()
141
+ : between?.mode === "static" || legacyBetween
142
+ ? (() => {
143
+ const start = legacyBetween
144
+ ? restrictions.between_start
145
+ : between.start;
146
+ const end = legacyBetween ? restrictions.between_end : between.end;
147
+ const startError = validateStaticSide(start, field.type);
148
+ const endError = validateStaticSide(end, field.type);
149
+ const orderError =
150
+ !startError &&
151
+ !endError &&
152
+ betweenOrderIsInvalid({ mode: "static", start, end }, field.type)
153
+ ? RESTRICTION_ERRORS.invalidOrder
154
+ : null;
155
+
156
+ return [
157
+ ["between.start", startError],
158
+ ["between.end", endError || orderError],
159
+ ];
160
+ })()
161
+ : between
162
+ ? [["mode", RESTRICTION_ERRORS.invalidMode]]
163
+ : [];
164
+
165
+ return Object.fromEntries(
166
+ [...restrictionErrors, ...betweenErrors].filter(([, error]) => error),
167
+ );
168
+ };
169
+
170
+ export const defaultValueOutOfRange = (field) => {
171
+ if (!isDateField(field)) return false;
172
+ if (_.isEmpty(field.restrictions)) return false;
173
+
174
+ const value = _.pathOr(null, ["default", "value"])(field);
175
+ if (value === null || value === undefined || value === "") return false;
176
+
177
+ const { min, max } = resolveRestrictions(field.restrictions, field.type);
178
+ return checkValuesAgainstBounds(value, { min, max }) !== "ok";
179
+ };
180
+
5
181
  const reduceField = (field) => {
6
182
  if (field.type === "dynamic_table") {
7
183
  const tableColumns = _.defaultTo([])(field?.values?.table_columns);
@@ -103,6 +279,10 @@ export const parseContentValidation = (content) => {
103
279
  !_.isEmpty(field.mandatory.on) &&
104
280
  _.isEmpty(field.mandatory.to_be);
105
281
 
282
+ const defaultOutOfRange = defaultValueOutOfRange(field);
283
+ const restrictions = validateDateRestrictions(field);
284
+ const restrictionsInvalid = !_.isEmpty(restrictions);
285
+
106
286
  const hasErrors = arrayAnd([
107
287
  nameDuplicated,
108
288
  nameEmpty,
@@ -111,10 +291,14 @@ export const parseContentValidation = (content) => {
111
291
  tableColumnEmpty,
112
292
  conditionalVisibilityValueEmpty,
113
293
  mandatoryDependingOnValueEmpty,
294
+ defaultOutOfRange,
295
+ restrictionsInvalid,
114
296
  ]);
115
297
  const errors = {
116
298
  hasErrors,
117
299
  nameDuplicated,
300
+ defaultOutOfRange,
301
+ restrictions,
118
302
  };
119
303
 
120
304
  if (field?.type === "dynamic_table") {