@oneblink/apps-react 11.1.2-beta.2 → 11.2.0-beta.1

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 (29) hide show
  1. package/dist/PaymentReceipt.js +12 -8
  2. package/dist/PaymentReceipt.js.map +1 -1
  3. package/dist/apps/payment-service.d.ts +2 -7
  4. package/dist/apps/payment-service.js +17 -14
  5. package/dist/apps/payment-service.js.map +1 -1
  6. package/dist/apps/scheduling-service.js +0 -1
  7. package/dist/apps/scheduling-service.js.map +1 -1
  8. package/dist/apps/services/api/payment.d.ts +8 -4
  9. package/dist/apps/services/api/payment.js +5 -6
  10. package/dist/apps/services/api/payment.js.map +1 -1
  11. package/dist/apps/services/payment-providers/BPOINTPaymentProvider.d.ts +1 -13
  12. package/dist/apps/services/payment-providers/BPOINTPaymentProvider.js +0 -14
  13. package/dist/apps/services/payment-providers/BPOINTPaymentProvider.js.map +1 -1
  14. package/dist/apps/services/payment-providers/CPPayPaymentProvider.d.ts +1 -11
  15. package/dist/apps/services/payment-providers/CPPayPaymentProvider.js +0 -9
  16. package/dist/apps/services/payment-providers/CPPayPaymentProvider.js.map +1 -1
  17. package/dist/apps/services/payment-providers/NSWGovPayPaymentProvider.d.ts +1 -14
  18. package/dist/apps/services/payment-providers/NSWGovPayPaymentProvider.js +0 -15
  19. package/dist/apps/services/payment-providers/NSWGovPayPaymentProvider.js.map +1 -1
  20. package/dist/apps/services/payment-providers/WestpacQuickStreamPaymentProvider.d.ts +1 -5
  21. package/dist/apps/services/payment-providers/WestpacQuickStreamPaymentProvider.js +0 -12
  22. package/dist/apps/services/payment-providers/WestpacQuickStreamPaymentProvider.js.map +1 -1
  23. package/dist/apps/services/submit.js +7 -8
  24. package/dist/apps/services/submit.js.map +1 -1
  25. package/dist/apps/types/payments.d.ts +0 -10
  26. package/dist/apps/types/payments.js.map +1 -1
  27. package/dist/form-elements/FormElementCalculation.js +22 -193
  28. package/dist/form-elements/FormElementCalculation.js.map +1 -1
  29. package/package.json +2 -4
@@ -1,26 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import * as React from 'react';
3
- import ExpressionParser from 'morph-expressions';
4
- import escapeString from 'escape-string-regexp';
5
3
  import useFormSubmissionModel from '../hooks/useFormSubmissionModelContext';
6
4
  import { Sentry } from '../apps';
7
5
  import { localisationService } from '../apps';
8
- import { formElementsService } from '@oneblink/sdk-core';
6
+ import { calculationService } from '@oneblink/sdk-core';
9
7
  import QuillHTML from '../components/QuillHTML';
10
8
  import MaterialIcon from '../components/MaterialIcon';
11
9
  import useFormDefinition from '../hooks/useFormDefinition';
12
- const isUnenteredValue = (value) => {
13
- return !value && value !== 0;
14
- };
15
- const isObjectWithValue = (obj) => {
16
- return typeof obj === 'object' && obj !== null && 'value' in obj;
17
- };
18
- // workaround for .toFixed() not rounding floating point numbers correctly
19
- function roundToFixed(number, decimals) {
20
- const multiplier = Math.pow(10, decimals);
21
- const roundedNumber = Math.round(number * multiplier) / multiplier;
22
- return roundedNumber.toFixed(decimals);
23
- }
24
10
  function FormElementCalculation({ element, onChange, value }) {
25
11
  const { formSubmissionModel } = useFormSubmissionModel();
26
12
  const form = useFormDefinition();
@@ -30,163 +16,30 @@ function FormElementCalculation({ element, onChange, value }) {
30
16
  htmlTemplate = element.defaultValue;
31
17
  }
32
18
  else {
33
- console.log('[Calculation] Was not a number... setting pre-calculation display');
34
19
  htmlTemplate = element.preCalculationDisplay;
35
20
  }
36
21
  const numberValue = typeof value === 'number' ? value : 0;
37
22
  return (htmlTemplate || '').replace(/{result}/gi, element.displayAsCurrency
38
23
  ? localisationService.formatCurrency(numberValue)
39
24
  : localisationService.formatNumber(numberValue));
40
- }, [element, value]);
41
- const registerProperty = React.useCallback((exprParser, { replacement, nestedElementNames, }) => {
42
- exprParser.registerProperty(replacement, (submission) => {
43
- const defaultAccumulator = submission[nestedElementNames[0]];
44
- return nestedElementNames.reduce((elementValue, elementName, index) => {
45
- // Numbers can just be returned as is
46
- if (typeof elementValue === 'number') {
47
- return elementValue;
48
- }
49
- // attempt to get a number from the element value as a string.
50
- // NaN is accounted for is the calculation
51
- // so we can return that from here
52
- if (typeof elementValue === 'string') {
53
- // The string could be an iso date string, or a string
54
- // resembling a date. We need to parse the value as an ISO string
55
- // and as a date in the format below to cover all calculation checks
56
- // with 'date', 'datetime' and 'time' elements. If the string is not
57
- // one of these, then we want to parse it as a float.
58
- const parsedIsoDate = localisationService.generateDate({
59
- value: elementValue,
60
- daysOffset: undefined,
61
- });
62
- if (parsedIsoDate) {
63
- return parsedIsoDate.getTime();
64
- }
65
- const parsedDate = localisationService.generateDate({
66
- value: elementValue,
67
- daysOffset: undefined,
68
- });
69
- if (parsedDate) {
70
- return parsedDate.getTime();
71
- }
72
- return parseFloat(elementValue);
73
- }
74
- if (Array.isArray(elementValue)) {
75
- // If there are no entries, we can return null
76
- // to prevent the calculation from running.
77
- if (!elementValue.length) {
78
- return NaN;
79
- }
80
- // An array could be an element that allows multiple
81
- // values e.g. checkboxes. If thats that case, we just
82
- // add them all together and move on
83
- const elementValues = elementValue.map((entry) => parseFloat(entry));
84
- if (elementValues.every((entry) => !Number.isNaN(entry))) {
85
- return elementValues.reduce((number, entry) => number + entry, 0);
86
- }
87
- // Other wise attempt to process it as a repeatable set
88
- // If we found another repeatable set to process,
89
- // pass it to the next element name to
90
- // iterate over the entries
91
- // If we are processing the entries in a repeatable set,
92
- // we can sum the numbers elements in the entries
93
- const nextElementName = nestedElementNames[index + 1];
94
- let isNestedRepeatableSet = false;
95
- const nestedElementValues = elementValue.reduce((nestedElementValues, entry) => {
96
- if (entry) {
97
- const nextElementValue = entry[nextElementName];
98
- if (Array.isArray(nextElementValue)) {
99
- if (nextElementValue.length) {
100
- nestedElementValues.push(...nextElementValue);
101
- isNestedRepeatableSet = true;
102
- }
103
- }
104
- else {
105
- nestedElementValues.push(nextElementValue);
106
- }
107
- }
108
- return nestedElementValues;
109
- }, []);
110
- // If the nested element values are all arrays, we can pass them on to the next iteration
111
- if (isNestedRepeatableSet) {
112
- return nestedElementValues;
113
- }
114
- return nestedElementValues.reduce((total, nestedElementValue) => {
115
- if (Number.isNaN(total)) {
116
- return NaN;
117
- }
118
- const value = parseFloat(nestedElementValue);
119
- if (Number.isNaN(value)) {
120
- return NaN;
121
- }
122
- return total + value;
123
- }, 0);
124
- }
125
- // if the value is an object, we take the element name and check to see
126
- // if this element is a nested form element. If so, we take the next nested element name,
127
- // find its value in the object and return it for the next iteration to handle
128
- if (typeof elementValue === 'object') {
129
- const formFormElement = formElementsService.findFormElement(form.elements, (e) => e.type === 'form' && e.name === nestedElementNames[index]);
130
- const nextElementName = nestedElementNames[index + 1];
131
- if (formFormElement && nextElementName) {
132
- return elementValue[nextElementName];
133
- }
134
- }
135
- // "compliance" form element has an object value with a "value" property.
136
- if (isObjectWithValue(elementValue) &&
137
- typeof elementValue.value === 'string') {
138
- return parseFloat(elementValue.value);
139
- }
140
- // We did not find a number value from the known elements,
141
- // we will assume we are at the end of the line.
142
- return NaN;
143
- }, defaultAccumulator);
144
- });
145
- }, [form.elements]);
146
- const { calculation, hasError } = React.useMemo(() => {
147
- const exprParser = new ExpressionParser();
148
- exprParser.registerFunction('ROUND', (value, precision) => {
149
- if (!Number.isNaN(value) && Number.isFinite(value)) {
150
- return parseFloat(roundToFixed(value, precision));
151
- }
152
- return null;
153
- });
154
- exprParser.registerFunction('ROUND_DOWN', (value) => {
155
- if (!Number.isNaN(value) && Number.isFinite(value)) {
156
- return Math.floor(value);
157
- }
158
- return null;
159
- });
160
- exprParser.registerFunction('ROUND_UP', (value) => {
161
- if (!Number.isNaN(value) && Number.isFinite(value)) {
162
- return Math.ceil(value);
163
- }
164
- return null;
165
- });
166
- exprParser.registerFunction('ISNULL', (value, defaultValue) => {
167
- if (isUnenteredValue(value)) {
168
- return defaultValue || 0;
169
- }
170
- return value;
171
- });
25
+ }, [
26
+ element.defaultValue,
27
+ element.displayAsCurrency,
28
+ element.preCalculationDisplay,
29
+ value,
30
+ ]);
31
+ const { calculatedValue, hasError } = React.useMemo(() => {
172
32
  try {
173
- if (!element.calculation)
33
+ if (!element.calculation) {
174
34
  throw new Error('Element has no calculation.');
175
- const elementNames = [];
176
- formElementsService.matchElementsTagRegex(element.calculation, ({ elementName }) => {
177
- elementNames.push(elementName);
178
- });
179
- const code = elementNames.reduce((code, elementName, index) => {
180
- const regex = new RegExp(escapeString(`{ELEMENT:${elementName}}`), 'g');
181
- const replacement = `a${index}`;
182
- registerProperty(exprParser, {
183
- replacement,
184
- nestedElementNames: elementName.split('|'),
185
- });
186
- return code.replace(regex, replacement);
187
- }, element.calculation || '');
35
+ }
188
36
  return {
189
- calculation: exprParser.parse(code.trim()),
37
+ calculatedValue: calculationService.evaluateExpression({
38
+ expression: element.calculation,
39
+ submission: formSubmissionModel,
40
+ formElements: form.elements,
41
+ parseDayOnlyDate: localisationService.parseDayOnlyDate,
42
+ }),
190
43
  hasError: false,
191
44
  };
192
45
  }
@@ -194,41 +47,17 @@ function FormElementCalculation({ element, onChange, value }) {
194
47
  console.warn('Error while setting up parsing for calculation element', element, e);
195
48
  Sentry.captureException(e);
196
49
  return {
197
- calculation: null,
50
+ calculatedValue: undefined,
198
51
  hasError: true,
199
52
  };
200
53
  }
201
- }, [element, registerProperty]);
202
- const calculateContent = React.useCallback((formSubmissionModel) => {
203
- if (!calculation) {
204
- return;
205
- }
206
- try {
207
- const result = calculation.eval(formSubmissionModel);
208
- return typeof result === 'number' ? result : undefined;
209
- }
210
- catch {
211
- //do nothing
212
- }
213
- return;
214
- }, [calculation]);
54
+ }, [element, form.elements, formSubmissionModel]);
215
55
  // MODEL LISTENER
216
56
  React.useEffect(() => {
217
- const newValue = calculateContent(formSubmissionModel);
218
- if (value === newValue || (value === undefined && Number.isNaN(newValue))) {
219
- return;
220
- }
221
- if (typeof newValue === 'number' && !Number.isNaN(newValue)) {
222
- onChange(element, {
223
- value: newValue,
224
- });
225
- }
226
- else {
227
- onChange(element, {
228
- value: undefined,
229
- });
230
- }
231
- }, [element, formSubmissionModel, onChange, value, calculateContent]);
57
+ onChange(element, {
58
+ value: Number.isNaN(calculatedValue) ? undefined : calculatedValue,
59
+ });
60
+ }, [element, onChange, calculatedValue]);
232
61
  return (_jsx("div", { className: "cypress-calculation-element", children: _jsxs("div", { className: "ob-form__element ob-calculation", children: [_jsx(QuillHTML, { html: htmlValue, className: "cypress-calculation-result ob-calculation__content" }), hasError && (_jsx("div", { className: "notification cypress-calculation-is-invalid", role: "alert", children: _jsxs("div", { className: "columns is-vcentered", children: [_jsx("div", { className: "column is-narrow", children: _jsx(MaterialIcon, { className: "has-text-warning", children: "error" }) }), _jsx("div", { className: "column", children: _jsx("p", { children: "There is an error in the calculation for this element." }) })] }) }))] }) }));
233
62
  }
234
63
  export default React.memo(FormElementCalculation);
@@ -1 +1 @@
1
- {"version":3,"file":"FormElementCalculation.js","sourceRoot":"","sources":["../../src/form-elements/FormElementCalculation.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react'\nimport ExpressionParser from 'morph-expressions'\nimport escapeString from 'escape-string-regexp'\nimport useFormSubmissionModel from '../hooks/useFormSubmissionModelContext'\nimport { FormTypes, SubmissionTypes } from '@oneblink/types'\nimport { Sentry } from '../apps'\nimport { localisationService } from '../apps'\nimport { FormElementValueChangeHandler } from '../types/form'\nimport { formElementsService } from '@oneblink/sdk-core'\nimport QuillHTML from '../components/QuillHTML'\nimport MaterialIcon from '../components/MaterialIcon'\nimport useFormDefinition from '../hooks/useFormDefinition'\n\ntype Props = {\n element: FormTypes.CalculationElement\n onChange: FormElementValueChangeHandler<number>\n value: unknown | undefined\n}\n\nconst isUnenteredValue = (value: unknown | undefined) => {\n return !value && value !== 0\n}\n\nconst isObjectWithValue = (obj: unknown): obj is { value: unknown } => {\n return typeof obj === 'object' && obj !== null && 'value' in obj\n}\n\n// workaround for .toFixed() not rounding floating point numbers correctly\nfunction roundToFixed(number: number, decimals: number) {\n const multiplier = Math.pow(10, decimals)\n const roundedNumber = Math.round(number * multiplier) / multiplier\n return roundedNumber.toFixed(decimals)\n}\n\nfunction FormElementCalculation({ element, onChange, value }: Props) {\n const { formSubmissionModel } = useFormSubmissionModel()\n const form = useFormDefinition()\n\n const htmlValue = React.useMemo(() => {\n let htmlTemplate\n\n if (!isNaN(value as number)) {\n htmlTemplate = element.defaultValue\n } else {\n console.log(\n '[Calculation] Was not a number... setting pre-calculation display',\n )\n htmlTemplate = element.preCalculationDisplay\n }\n\n const numberValue = typeof value === 'number' ? value : 0\n return (htmlTemplate || '').replace(\n /{result}/gi,\n element.displayAsCurrency\n ? localisationService.formatCurrency(numberValue)\n : localisationService.formatNumber(numberValue),\n )\n }, [element, value])\n\n const registerProperty = React.useCallback(\n (\n exprParser: ExpressionParser<\n SubmissionTypes.S3SubmissionData['submission']\n >,\n {\n replacement,\n nestedElementNames,\n }: {\n replacement: string\n nestedElementNames: string[]\n },\n ) => {\n exprParser.registerProperty(\n replacement,\n (submission: SubmissionTypes.S3SubmissionData['submission']) => {\n const defaultAccumulator = submission[nestedElementNames[0]]\n return nestedElementNames.reduce(\n (\n elementValue: unknown | undefined,\n elementName: string,\n index: number,\n ) => {\n // Numbers can just be returned as is\n if (typeof elementValue === 'number') {\n return elementValue\n }\n\n // attempt to get a number from the element value as a string.\n // NaN is accounted for is the calculation\n // so we can return that from here\n if (typeof elementValue === 'string') {\n // The string could be an iso date string, or a string\n // resembling a date. We need to parse the value as an ISO string\n // and as a date in the format below to cover all calculation checks\n // with 'date', 'datetime' and 'time' elements. If the string is not\n // one of these, then we want to parse it as a float.\n\n const parsedIsoDate = localisationService.generateDate({\n value: elementValue,\n daysOffset: undefined,\n })\n if (parsedIsoDate) {\n return parsedIsoDate.getTime()\n }\n const parsedDate = localisationService.generateDate({\n value: elementValue,\n daysOffset: undefined,\n })\n if (parsedDate) {\n return parsedDate.getTime()\n }\n\n return parseFloat(elementValue)\n }\n\n if (Array.isArray(elementValue)) {\n // If there are no entries, we can return null\n // to prevent the calculation from running.\n if (!elementValue.length) {\n return NaN\n }\n\n // An array could be an element that allows multiple\n // values e.g. checkboxes. If thats that case, we just\n // add them all together and move on\n const elementValues = elementValue.map((entry) =>\n parseFloat(entry),\n )\n if (elementValues.every((entry) => !Number.isNaN(entry))) {\n return elementValues.reduce(\n (number, entry) => number + entry,\n 0,\n )\n }\n\n // Other wise attempt to process it as a repeatable set\n // If we found another repeatable set to process,\n // pass it to the next element name to\n // iterate over the entries\n\n // If we are processing the entries in a repeatable set,\n // we can sum the numbers elements in the entries\n const nextElementName = nestedElementNames[index + 1]\n\n let isNestedRepeatableSet = false\n const nestedElementValues = elementValue.reduce(\n (nestedElementValues, entry) => {\n if (entry) {\n const nextElementValue = entry[nextElementName]\n if (Array.isArray(nextElementValue)) {\n if (nextElementValue.length) {\n nestedElementValues.push(...nextElementValue)\n isNestedRepeatableSet = true\n }\n } else {\n nestedElementValues.push(nextElementValue)\n }\n }\n return nestedElementValues\n },\n [],\n )\n\n // If the nested element values are all arrays, we can pass them on to the next iteration\n if (isNestedRepeatableSet) {\n return nestedElementValues\n }\n\n return nestedElementValues.reduce(\n (total: number, nestedElementValue: unknown | undefined) => {\n if (Number.isNaN(total)) {\n return NaN\n }\n const value = parseFloat(nestedElementValue as string)\n if (Number.isNaN(value)) {\n return NaN\n }\n return total + value\n },\n 0,\n )\n }\n\n // if the value is an object, we take the element name and check to see\n // if this element is a nested form element. If so, we take the next nested element name,\n // find its value in the object and return it for the next iteration to handle\n if (typeof elementValue === 'object') {\n const formFormElement = formElementsService.findFormElement(\n form.elements,\n (e) =>\n e.type === 'form' && e.name === nestedElementNames[index],\n )\n const nextElementName = nestedElementNames[index + 1]\n if (formFormElement && nextElementName) {\n return (elementValue as Record<string, unknown>)[\n nextElementName\n ]\n }\n }\n\n // \"compliance\" form element has an object value with a \"value\" property.\n if (\n isObjectWithValue(elementValue) &&\n typeof elementValue.value === 'string'\n ) {\n return parseFloat(elementValue.value)\n }\n\n // We did not find a number value from the known elements,\n // we will assume we are at the end of the line.\n return NaN\n },\n defaultAccumulator,\n )\n },\n )\n },\n [form.elements],\n )\n\n const { calculation, hasError } = React.useMemo(() => {\n const exprParser = new ExpressionParser<\n SubmissionTypes.S3SubmissionData['submission']\n >()\n exprParser.registerFunction('ROUND', (value: number, precision: number) => {\n if (!Number.isNaN(value) && Number.isFinite(value)) {\n return parseFloat(roundToFixed(value, precision))\n }\n return null\n })\n exprParser.registerFunction('ROUND_DOWN', (value: number) => {\n if (!Number.isNaN(value) && Number.isFinite(value)) {\n return Math.floor(value)\n }\n return null\n })\n exprParser.registerFunction('ROUND_UP', (value: number) => {\n if (!Number.isNaN(value) && Number.isFinite(value)) {\n return Math.ceil(value)\n }\n return null\n })\n exprParser.registerFunction(\n 'ISNULL',\n (value: unknown | undefined, defaultValue: number) => {\n if (isUnenteredValue(value)) {\n return defaultValue || 0\n }\n return value\n },\n )\n\n try {\n if (!element.calculation) throw new Error('Element has no calculation.')\n const elementNames: string[] = []\n formElementsService.matchElementsTagRegex(\n element.calculation,\n ({ elementName }) => {\n elementNames.push(elementName)\n },\n )\n\n const code = elementNames.reduce((code, elementName, index) => {\n const regex = new RegExp(escapeString(`{ELEMENT:${elementName}}`), 'g')\n const replacement = `a${index}`\n registerProperty(exprParser, {\n replacement,\n nestedElementNames: elementName.split('|'),\n })\n return code.replace(regex, replacement)\n }, element.calculation || '')\n\n return {\n calculation: exprParser.parse(code.trim()),\n hasError: false,\n }\n } catch (e) {\n console.warn(\n 'Error while setting up parsing for calculation element',\n element,\n e,\n )\n Sentry.captureException(e)\n return {\n calculation: null,\n hasError: true,\n }\n }\n }, [element, registerProperty])\n\n const calculateContent = React.useCallback(\n (formSubmissionModel: SubmissionTypes.S3SubmissionData['submission']) => {\n if (!calculation) {\n return\n }\n try {\n const result = calculation.eval(formSubmissionModel)\n return typeof result === 'number' ? result : undefined\n } catch {\n //do nothing\n }\n return\n },\n [calculation],\n )\n\n // MODEL LISTENER\n React.useEffect(() => {\n const newValue = calculateContent(formSubmissionModel)\n if (value === newValue || (value === undefined && Number.isNaN(newValue))) {\n return\n }\n if (typeof newValue === 'number' && !Number.isNaN(newValue)) {\n onChange(element, {\n value: newValue,\n })\n } else {\n onChange(element, {\n value: undefined,\n })\n }\n }, [element, formSubmissionModel, onChange, value, calculateContent])\n\n return (\n <div className=\"cypress-calculation-element\">\n <div className=\"ob-form__element ob-calculation\">\n <QuillHTML\n html={htmlValue}\n className=\"cypress-calculation-result ob-calculation__content\"\n />\n {hasError && (\n <div\n className=\"notification cypress-calculation-is-invalid\"\n role=\"alert\"\n >\n <div className=\"columns is-vcentered\">\n <div className=\"column is-narrow\">\n <MaterialIcon className=\"has-text-warning\">error</MaterialIcon>\n </div>\n <div className=\"column\">\n <p>There is an error in the calculation for this element.</p>\n </div>\n </div>\n </div>\n )}\n </div>\n </div>\n )\n}\n\nexport default React.memo(FormElementCalculation)\n"]}
1
+ {"version":3,"file":"FormElementCalculation.js","sourceRoot":"","sources":["../../src/form-elements/FormElementCalculation.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react'\nimport useFormSubmissionModel from '../hooks/useFormSubmissionModelContext'\nimport { FormTypes } from '@oneblink/types'\nimport { Sentry } from '../apps'\nimport { localisationService } from '../apps'\nimport { FormElementValueChangeHandler } from '../types/form'\nimport { calculationService } from '@oneblink/sdk-core'\nimport QuillHTML from '../components/QuillHTML'\nimport MaterialIcon from '../components/MaterialIcon'\nimport useFormDefinition from '../hooks/useFormDefinition'\n\ntype Props = {\n element: FormTypes.CalculationElement\n onChange: FormElementValueChangeHandler<number>\n value: unknown | undefined\n}\n\nfunction FormElementCalculation({ element, onChange, value }: Props) {\n const { formSubmissionModel } = useFormSubmissionModel()\n const form = useFormDefinition()\n\n const htmlValue = React.useMemo(() => {\n let htmlTemplate\n\n if (!isNaN(value as number)) {\n htmlTemplate = element.defaultValue\n } else {\n htmlTemplate = element.preCalculationDisplay\n }\n\n const numberValue = typeof value === 'number' ? value : 0\n return (htmlTemplate || '').replace(\n /{result}/gi,\n element.displayAsCurrency\n ? localisationService.formatCurrency(numberValue)\n : localisationService.formatNumber(numberValue),\n )\n }, [\n element.defaultValue,\n element.displayAsCurrency,\n element.preCalculationDisplay,\n value,\n ])\n\n const { calculatedValue, hasError } = React.useMemo(() => {\n try {\n if (!element.calculation) {\n throw new Error('Element has no calculation.')\n }\n\n return {\n calculatedValue: calculationService.evaluateExpression({\n expression: element.calculation,\n submission: formSubmissionModel,\n formElements: form.elements,\n parseDayOnlyDate: localisationService.parseDayOnlyDate,\n }),\n hasError: false,\n }\n } catch (e) {\n console.warn(\n 'Error while setting up parsing for calculation element',\n element,\n e,\n )\n Sentry.captureException(e)\n return {\n calculatedValue: undefined,\n hasError: true,\n }\n }\n }, [element, form.elements, formSubmissionModel])\n\n // MODEL LISTENER\n React.useEffect(() => {\n onChange(element, {\n value: Number.isNaN(calculatedValue) ? undefined : calculatedValue,\n })\n }, [element, onChange, calculatedValue])\n\n return (\n <div className=\"cypress-calculation-element\">\n <div className=\"ob-form__element ob-calculation\">\n <QuillHTML\n html={htmlValue}\n className=\"cypress-calculation-result ob-calculation__content\"\n />\n {hasError && (\n <div\n className=\"notification cypress-calculation-is-invalid\"\n role=\"alert\"\n >\n <div className=\"columns is-vcentered\">\n <div className=\"column is-narrow\">\n <MaterialIcon className=\"has-text-warning\">error</MaterialIcon>\n </div>\n <div className=\"column\">\n <p>There is an error in the calculation for this element.</p>\n </div>\n </div>\n </div>\n )}\n </div>\n </div>\n )\n}\n\nexport default React.memo(FormElementCalculation)\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@oneblink/apps-react",
3
3
  "description": "Helper functions for OneBlink apps in ReactJS.",
4
- "version": "11.1.2-beta.2",
4
+ "version": "11.2.0-beta.1",
5
5
  "author": "OneBlink <developers@oneblink.io> (https://oneblink.io)",
6
6
  "bugs": {
7
7
  "url": "https://github.com/oneblink/apps-react/issues"
@@ -12,7 +12,7 @@
12
12
  "@emotion/react": "^11.14.0",
13
13
  "@emotion/styled": "^11.14.1",
14
14
  "@nylas/react": "^3.1.2",
15
- "@oneblink/sdk-core": "^10.1.0-beta.1",
15
+ "@oneblink/sdk-core": "^10.1.0-beta.2",
16
16
  "@oneblink/storage": "^7.1.2-beta.2",
17
17
  "@react-google-maps/api": "^2.20.8",
18
18
  "@react-input/mask": "^2.0.4",
@@ -29,7 +29,6 @@
29
29
  "copy-to-clipboard": "^3.3.3",
30
30
  "date-fns": "^2.30.0",
31
31
  "email-validator": "^2.0.4",
32
- "escape-string-regexp": "^5.0.0",
33
32
  "file-saver": "^2.0.5",
34
33
  "file-type-checker": "^1.1.6",
35
34
  "html5-qrcode": "^2.3.8",
@@ -39,7 +38,6 @@
39
38
  "lodash.debounce": "^4.0.8",
40
39
  "lodash.orderby": "^4.6.0",
41
40
  "lodash.throttle": "^4.1.1",
42
- "morph-expressions": "^1.1.1",
43
41
  "qrcode.react": "^4.2.0",
44
42
  "query-string": "^8.2.0",
45
43
  "react-google-recaptcha": "^3.1.0",