@engagebay/engagebay-form-module 1.0.10 → 1.0.11
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.
- package/package.json +1 -1
- package/src/form/formfields/CheckboxField.tsx +30 -11
- package/src/form/formfields/DatePickerField.tsx +10 -9
- package/src/form/formfields/FileUploadField.tsx +8 -6
- package/src/form/formfields/MultipleSelectField.tsx +17 -5
- package/src/form/schema/FormFieldSchema.ts +3 -1
- package/src/form/util/RenderListOptions.tsx +13 -8
- package/src/form/util/normalizeCustomFieldValues.ts +149 -0
package/package.json
CHANGED
|
@@ -1,32 +1,46 @@
|
|
|
1
1
|
import { RegisterOptions } from "react-hook-form";
|
|
2
2
|
import { useContext, useEffect, useState } from "react";
|
|
3
3
|
import { FormContext } from "../context/FormContext";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
FormFieldComponentPropSchema,
|
|
6
|
+
OutputFormatType,
|
|
7
|
+
} from "../schema/FormFieldSchema";
|
|
5
8
|
import { Checkbox, Field, Input, Label } from "@headlessui/react";
|
|
6
9
|
import React from "react";
|
|
7
10
|
import RenderFormField from "../util/RenderFormField";
|
|
8
11
|
import { handleChange, registerFormField } from "../util";
|
|
12
|
+
import { normalizeCheckboxBooleanValue, normalizeMultiSelectValue } from "../util/normalizeCustomFieldValues";
|
|
9
13
|
const CheckboxField: React.FC<FormFieldComponentPropSchema> = (
|
|
10
14
|
props: FormFieldComponentPropSchema
|
|
11
15
|
) => {
|
|
12
16
|
const formContext = useContext(FormContext);
|
|
13
17
|
const [selectedOption, setSelectedOption] = useState<string[]>(
|
|
14
|
-
formContext.getValues(props.fieldConfig.name)
|
|
15
|
-
? formContext.getValues(props.fieldConfig.name)
|
|
16
|
-
: []
|
|
18
|
+
normalizeMultiSelectValue(formContext.getValues(props.fieldConfig.name))
|
|
17
19
|
);
|
|
18
20
|
const [isChecked, setIsChecked] = useState<boolean>(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
normalizeCheckboxBooleanValue(
|
|
22
|
+
formContext.getValues(props.fieldConfig.name) ?? props.fieldConfig.defaultValue
|
|
23
|
+
)
|
|
22
24
|
);
|
|
23
25
|
useEffect(() => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
const current = formContext.getValues(props.fieldConfig.name);
|
|
27
|
+
if (props.fieldConfig.options) {
|
|
28
|
+
setSelectedOption(normalizeMultiSelectValue(current));
|
|
29
|
+
} else {
|
|
30
|
+
setIsChecked(
|
|
31
|
+
normalizeCheckboxBooleanValue(current ?? props.fieldConfig.defaultValue)
|
|
32
|
+
);
|
|
26
33
|
}
|
|
27
34
|
}, []);
|
|
28
35
|
let registerOptions: RegisterOptions = registerFormField(props.fieldConfig);
|
|
29
36
|
let hookProps = formContext.register(props.fieldConfig.name, registerOptions);
|
|
37
|
+
const getOutputValue = (value: any) => {
|
|
38
|
+
if (props.fieldConfig.outputFormat === OutputFormatType.STRING) {
|
|
39
|
+
if (Array.isArray(value)) return JSON.stringify(value);
|
|
40
|
+
if (typeof value === "boolean") return String(value);
|
|
41
|
+
}
|
|
42
|
+
return value;
|
|
43
|
+
};
|
|
30
44
|
const handleInput = (option: string) => {
|
|
31
45
|
let tempData: string[] = selectedOption;
|
|
32
46
|
if (selectedOption.includes(option)) {
|
|
@@ -36,7 +50,12 @@ const CheckboxField: React.FC<FormFieldComponentPropSchema> = (
|
|
|
36
50
|
tempData.push(option);
|
|
37
51
|
setSelectedOption([...tempData]);
|
|
38
52
|
}
|
|
39
|
-
handleChange(
|
|
53
|
+
handleChange(
|
|
54
|
+
getOutputValue(tempData),
|
|
55
|
+
formContext,
|
|
56
|
+
props.fieldConfig,
|
|
57
|
+
props.onChange
|
|
58
|
+
);
|
|
40
59
|
};
|
|
41
60
|
function getInput() {
|
|
42
61
|
return props.fieldConfig.options ? (
|
|
@@ -87,7 +106,7 @@ const CheckboxField: React.FC<FormFieldComponentPropSchema> = (
|
|
|
87
106
|
checked={isChecked}
|
|
88
107
|
onChange={(checked) => {
|
|
89
108
|
handleChange(
|
|
90
|
-
checked,
|
|
109
|
+
getOutputValue(checked),
|
|
91
110
|
formContext,
|
|
92
111
|
props.fieldConfig,
|
|
93
112
|
props.onChange
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { RegisterOptions } from "react-hook-form";
|
|
2
|
-
import React, { useContext, useEffect
|
|
2
|
+
import React, { useContext, useEffect } from "react";
|
|
3
3
|
import Datepicker from "react-tailwindcss-datepicker";
|
|
4
4
|
import { FormContext } from "../context/FormContext";
|
|
5
5
|
import { FormFieldComponentPropSchema } from "../schema/FormFieldSchema";
|
|
6
6
|
import RenderFormField from "../util/RenderFormField";
|
|
7
7
|
import { handleChange, registerFormField } from "../util";
|
|
8
|
+
import { normalizeDateFormat, normalizeDateInputValue } from "../util/normalizeCustomFieldValues";
|
|
8
9
|
import moment from "moment";
|
|
9
10
|
|
|
11
|
+
|
|
12
|
+
|
|
10
13
|
const DatePicker: React.FC<FormFieldComponentPropSchema> = (
|
|
11
14
|
props: FormFieldComponentPropSchema
|
|
12
15
|
) => {
|
|
@@ -35,6 +38,7 @@ const DatePicker: React.FC<FormFieldComponentPropSchema> = (
|
|
|
35
38
|
let hookProps = formContext.register(props.fieldConfig.name, registerOptions);
|
|
36
39
|
|
|
37
40
|
function getInput() {
|
|
41
|
+
const displayFormat = normalizeDateFormat(props.fieldConfig.dateDisplayFormat);
|
|
38
42
|
const rawStart =
|
|
39
43
|
initialDate?.startDate ||
|
|
40
44
|
props.fieldConfig.defaultValue ||
|
|
@@ -43,13 +47,9 @@ const DatePicker: React.FC<FormFieldComponentPropSchema> = (
|
|
|
43
47
|
initialDate?.endDate ||
|
|
44
48
|
props.fieldConfig.defaultValue ||
|
|
45
49
|
"";
|
|
46
|
-
const toDate = (v: string | Date | null | undefined): Date | null => {
|
|
47
|
-
if (v == null || v === "") return null;
|
|
48
|
-
return moment(v).isValid() ? moment(v).toDate() : null;
|
|
49
|
-
};
|
|
50
50
|
const value = {
|
|
51
|
-
startDate:
|
|
52
|
-
endDate:
|
|
51
|
+
startDate: normalizeDateInputValue(rawStart, displayFormat),
|
|
52
|
+
endDate: normalizeDateInputValue(rawEnd, displayFormat),
|
|
53
53
|
};
|
|
54
54
|
// Calendar opens on selected date's month/year (must be a Date instance)
|
|
55
55
|
const startFrom = value.startDate instanceof Date ? value.startDate : new Date();
|
|
@@ -58,10 +58,11 @@ const DatePicker: React.FC<FormFieldComponentPropSchema> = (
|
|
|
58
58
|
value={value}
|
|
59
59
|
startFrom={startFrom}
|
|
60
60
|
{...hookProps}
|
|
61
|
+
displayFormat={displayFormat}
|
|
61
62
|
placeholder={
|
|
62
63
|
props.fieldConfig.placeholder
|
|
63
64
|
? props.fieldConfig.placeholder
|
|
64
|
-
:
|
|
65
|
+
: displayFormat
|
|
65
66
|
}
|
|
66
67
|
asSingle={true}
|
|
67
68
|
popoverDirection="down"
|
|
@@ -79,7 +80,7 @@ const DatePicker: React.FC<FormFieldComponentPropSchema> = (
|
|
|
79
80
|
onChange={(dates) => {
|
|
80
81
|
let date = null;
|
|
81
82
|
if (dates?.startDate != null)
|
|
82
|
-
date = moment(dates?.startDate).format(
|
|
83
|
+
date = moment(dates?.startDate).format(displayFormat);
|
|
83
84
|
|
|
84
85
|
handleChange(date, formContext, props.fieldConfig, props.onChange);
|
|
85
86
|
}}
|
|
@@ -9,6 +9,8 @@ import { Description } from "@headlessui/react";
|
|
|
9
9
|
import React from "react";
|
|
10
10
|
import RenderFormField from "../util/RenderFormField";
|
|
11
11
|
import clsx from "clsx";
|
|
12
|
+
import { XMarkIcon } from "@heroicons/react/20/solid";
|
|
13
|
+
import { normalizeFileFieldUrls } from "../util/normalizeCustomFieldValues";
|
|
12
14
|
const FileUploadField: React.FC<FormFieldComponentPropSchema> = (
|
|
13
15
|
props: FormFieldComponentPropSchema
|
|
14
16
|
) => {
|
|
@@ -103,7 +105,7 @@ const FileUploadField: React.FC<FormFieldComponentPropSchema> = (
|
|
|
103
105
|
<Description>Loading...</Description>
|
|
104
106
|
) : (
|
|
105
107
|
props.fieldConfig.fieldContainer ? <props.fieldConfig.fieldContainer /> :
|
|
106
|
-
<div className={"flex flex-col items-center justify-center pt-5 pb-6"}>
|
|
108
|
+
<div className={"flex flex-col items-center justify-center pt-5 pb-6 px-2"}>
|
|
107
109
|
{file ? (
|
|
108
110
|
<>
|
|
109
111
|
{isImageFile(file) ? (
|
|
@@ -125,19 +127,19 @@ const FileUploadField: React.FC<FormFieldComponentPropSchema> = (
|
|
|
125
127
|
</div>
|
|
126
128
|
) : (
|
|
127
129
|
<div className="flex">
|
|
128
|
-
<span className="m-2">
|
|
129
|
-
|
|
130
|
+
<span className="m-2 max-w-xs truncate">
|
|
131
|
+
{normalizeFileFieldUrls(file?.name || file?.toString?.() || '')}
|
|
130
132
|
</span>{" "}
|
|
131
133
|
<button
|
|
132
134
|
onClick={(e) => {
|
|
133
135
|
e.preventDefault();
|
|
134
136
|
formContext.setValue(
|
|
135
|
-
props.fieldConfig.name,
|
|
137
|
+
props.fieldConfig.name,null
|
|
136
138
|
);
|
|
137
139
|
setFile(null);
|
|
138
140
|
}}
|
|
139
|
-
className="btn btn-
|
|
140
|
-
|
|
141
|
+
className="btn btn-sm ">
|
|
142
|
+
<XMarkIcon className="w-3.5 h-3.5 text-gray-800"/>
|
|
141
143
|
</button>
|
|
142
144
|
</div>
|
|
143
145
|
)}
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
FieldOptionsSchema,
|
|
14
14
|
FormFieldComponentPropSchema,
|
|
15
15
|
FormFieldType,
|
|
16
|
+
OutputFormatType,
|
|
16
17
|
} from "../schema/FormFieldSchema";
|
|
17
18
|
import { FormContext } from "../context/FormContext";
|
|
18
19
|
import RenderFormField from "../util/RenderFormField";
|
|
@@ -20,6 +21,7 @@ import RenderListOptions, {
|
|
|
20
21
|
renderListBoxValue,
|
|
21
22
|
} from "../util/RenderListOptions";
|
|
22
23
|
import { handleChange, registerFormField } from "../util";
|
|
24
|
+
import { normalizeMultiSelectValue } from "../util/normalizeCustomFieldValues";
|
|
23
25
|
|
|
24
26
|
const MultipleSelectField: React.FC<FormFieldComponentPropSchema> = ({
|
|
25
27
|
fieldConfig,
|
|
@@ -34,9 +36,11 @@ const MultipleSelectField: React.FC<FormFieldComponentPropSchema> = ({
|
|
|
34
36
|
fieldConfig.options ? fieldConfig.options : []
|
|
35
37
|
);
|
|
36
38
|
|
|
37
|
-
const
|
|
38
|
-
formContext.getValues(fieldConfig.name)
|
|
39
|
-
)
|
|
39
|
+
const normalizedSelectedValues = normalizeMultiSelectValue(
|
|
40
|
+
formContext.getValues(fieldConfig.name)
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const validSelectedValues = normalizedSelectedValues.filter(
|
|
40
44
|
(selectedValue: any) =>
|
|
41
45
|
fieldConfig.options &&
|
|
42
46
|
fieldConfig.options.some((option) => option.value === selectedValue)
|
|
@@ -53,14 +57,22 @@ const MultipleSelectField: React.FC<FormFieldComponentPropSchema> = ({
|
|
|
53
57
|
}, [fieldConfig.options]);
|
|
54
58
|
|
|
55
59
|
function getInput() {
|
|
60
|
+
const getOutputValue = (val: any) => {
|
|
61
|
+
if (fieldConfig.outputFormat === OutputFormatType.STRING) {
|
|
62
|
+
const normalized = normalizeMultiSelectValue(val);
|
|
63
|
+
return JSON.stringify(normalized);
|
|
64
|
+
}
|
|
65
|
+
return val;
|
|
66
|
+
};
|
|
67
|
+
|
|
56
68
|
return (
|
|
57
69
|
<Listbox
|
|
58
70
|
as={"div"}
|
|
59
71
|
{...hookProps}
|
|
60
72
|
value={validSelectedValues}
|
|
61
73
|
defaultValue={fieldConfig.defaultValue}
|
|
62
|
-
onChange={(val) =>
|
|
63
|
-
handleChange(val, formContext, fieldConfig, onChange)
|
|
74
|
+
onChange={(val: any) =>
|
|
75
|
+
handleChange(getOutputValue(val), formContext, fieldConfig, onChange)
|
|
64
76
|
}
|
|
65
77
|
disabled={fieldConfig.disabled}
|
|
66
78
|
multiple
|
|
@@ -111,6 +111,8 @@ export type FormFieldSchema = {
|
|
|
111
111
|
options?: FieldOptionsSchema[];
|
|
112
112
|
minDate?: Date | null | undefined;
|
|
113
113
|
maxDate?: Date | null | undefined;
|
|
114
|
+
/** Input display format for date pickers (e.g. 'MMMM D, YYYY'). */
|
|
115
|
+
dateDisplayFormat?: string;
|
|
114
116
|
// ref?:any
|
|
115
117
|
|
|
116
118
|
/**
|
|
@@ -240,7 +242,7 @@ export class FormFieldPatternsImpl implements FormFieldPatterns {
|
|
|
240
242
|
static readonly EMAIL = new FormFieldPatternsImpl(
|
|
241
243
|
"EMAIL",
|
|
242
244
|
"Please enter a valid email address",
|
|
243
|
-
/^([
|
|
245
|
+
/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i
|
|
244
246
|
);
|
|
245
247
|
// static readonly PASSWORD = new FormFieldPatternsImpl(
|
|
246
248
|
// "PASSWORD",
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
FormFieldType,
|
|
19
19
|
OutputFormatType,
|
|
20
20
|
} from "../schema/FormFieldSchema";
|
|
21
|
-
|
|
21
|
+
import { normalizeMultiSelectValue, normalizeMultiSelectValueForStringArrayValues } from "./normalizeCustomFieldValues";
|
|
22
22
|
type RenderListOptionsProps = {
|
|
23
23
|
formContext: FormContextType;
|
|
24
24
|
fieldConfig: FormFieldSchema;
|
|
@@ -154,12 +154,17 @@ const RenderListOptions = forwardRef<HTMLUListElement, RenderListOptionsProps>(
|
|
|
154
154
|
|
|
155
155
|
const renderOption = (option: FieldOptionsSchema) => {
|
|
156
156
|
let selected = false;
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
157
|
+
const formValue = formContext.getValues(fieldConfig.name);
|
|
158
|
+
if (formField === FormFieldType.MULTI_SELECT) {
|
|
159
|
+
const normalizedValues = normalizeMultiSelectValue(formValue);
|
|
160
|
+
const normalizedOptionValue =
|
|
161
|
+
typeof option.value === "string"
|
|
162
|
+
? option.value.trim()
|
|
163
|
+
: String(option.value).trim();
|
|
164
|
+
selected = normalizedValues.includes(normalizedOptionValue);
|
|
165
|
+
} else if (Array.isArray(formValue)) {
|
|
166
|
+
selected = formValue.includes(option.value);
|
|
161
167
|
} else {
|
|
162
|
-
const formValue = formContext.getValues(fieldConfig.name);
|
|
163
168
|
// const defaultValue = fieldConfig.defaultValue;
|
|
164
169
|
// selected = formValue ? option.value == formValue : defaultValue == option.value;
|
|
165
170
|
selected = option.value == formValue;
|
|
@@ -335,7 +340,7 @@ export function renderListBoxValue(
|
|
|
335
340
|
listOptions: FieldOptionsSchema[],
|
|
336
341
|
onChange?: (value: any) => void,
|
|
337
342
|
): JSX.Element {
|
|
338
|
-
let value = formContext.getValues(fieldConfig.name);
|
|
343
|
+
let value = normalizeMultiSelectValueForStringArrayValues(fieldConfig, formContext.getValues(fieldConfig.name));
|
|
339
344
|
const renderAsString = () => {
|
|
340
345
|
// if (!listOptions) {
|
|
341
346
|
// return value;
|
|
@@ -437,7 +442,7 @@ export function renderListBoxValue(
|
|
|
437
442
|
if (!value && !listOptions) {
|
|
438
443
|
return getPlaceholder();
|
|
439
444
|
}
|
|
440
|
-
if (
|
|
445
|
+
if (Array.isArray(value)) {
|
|
441
446
|
return renderAsArray();
|
|
442
447
|
}
|
|
443
448
|
return renderAsString();
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import moment from "moment";
|
|
2
|
+
import { FormFieldSchema, OutputFormatType } from "../schema/FormFieldSchema";
|
|
3
|
+
|
|
4
|
+
const normalizeStringToken = (value: unknown): string => {
|
|
5
|
+
return typeof value === "string" ? value.trim() : String(value).trim();
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const normalizeStringArray = (values: unknown[]): string[] => {
|
|
9
|
+
return values.map(normalizeStringToken).filter(Boolean);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const parseJsonArrayString = (value: string): string[] | null => {
|
|
13
|
+
if (!value.startsWith("[")) return null;
|
|
14
|
+
try {
|
|
15
|
+
const parsed = JSON.parse(value);
|
|
16
|
+
return Array.isArray(parsed) ? normalizeStringArray(parsed) : null;
|
|
17
|
+
} catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const normalizeCheckboxBooleanValue = (value: any): boolean => {
|
|
23
|
+
if (value === true || value === 1) return true;
|
|
24
|
+
if (value === false || value === 0 || value == null || value === "") {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (typeof value === "string") {
|
|
29
|
+
const s = value.trim().toLowerCase();
|
|
30
|
+
if (s === "true" || s === "1" || s === "yes") return true;
|
|
31
|
+
if (s === "false" || s === "0" || s === "no" || s === "") return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return Boolean(value);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const normalizeMultiSelectValue = (value: any): string[] => {
|
|
38
|
+
if (value == null) return [];
|
|
39
|
+
|
|
40
|
+
if (Array.isArray(value)) return normalizeStringArray(value);
|
|
41
|
+
|
|
42
|
+
if (typeof value === "string") {
|
|
43
|
+
const s = value.trim();
|
|
44
|
+
if (!s) return [];
|
|
45
|
+
|
|
46
|
+
const jsonArray = parseJsonArrayString(s);
|
|
47
|
+
if (jsonArray) return jsonArray;
|
|
48
|
+
|
|
49
|
+
return normalizeStringArray(s.split(","));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return normalizeStringArray([value]);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const normalizeDateFormat = (format?: string): string => {
|
|
56
|
+
if (!format) return "YYYY-MM-DD";
|
|
57
|
+
return format
|
|
58
|
+
.replace(/yyyy/g, "YYYY")
|
|
59
|
+
.replace(/yy/g, "YY")
|
|
60
|
+
.replace(/dd/g, "DD")
|
|
61
|
+
.replace(/d/g, "D")
|
|
62
|
+
.replace(/mm/g, "MM");
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const normalizeDateInputValue = (
|
|
66
|
+
value: any,
|
|
67
|
+
preferredFormat?: string
|
|
68
|
+
): Date | null => {
|
|
69
|
+
if (value == null || value === "") return null;
|
|
70
|
+
|
|
71
|
+
if (value instanceof Date) {
|
|
72
|
+
return Number.isNaN(value.getTime()) ? null : value;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
76
|
+
const ms = value < 1_000_000_000_000 ? value * 1000 : value;
|
|
77
|
+
const date = new Date(ms);
|
|
78
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (typeof value === "string") {
|
|
82
|
+
const s = value.trim();
|
|
83
|
+
if (!s) return null;
|
|
84
|
+
|
|
85
|
+
const numeric = Number(s);
|
|
86
|
+
if (Number.isFinite(numeric)) {
|
|
87
|
+
const ms = numeric < 1_000_000_000_000 ? numeric * 1000 : numeric;
|
|
88
|
+
const date = new Date(ms);
|
|
89
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const preferred = normalizeDateFormat(preferredFormat);
|
|
93
|
+
const formats = Array.from(
|
|
94
|
+
new Set([
|
|
95
|
+
preferred,
|
|
96
|
+
"YYYY-MM-DD",
|
|
97
|
+
"DD/MM/YYYY",
|
|
98
|
+
"MM/DD/YYYY",
|
|
99
|
+
"DD-MM-YYYY",
|
|
100
|
+
"MM-DD-YYYY",
|
|
101
|
+
])
|
|
102
|
+
);
|
|
103
|
+
const parsedWithKnownFormats = moment(s, formats, true);
|
|
104
|
+
if (parsedWithKnownFormats.isValid()) {
|
|
105
|
+
return parsedWithKnownFormats.toDate();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const date = new Date(s);
|
|
109
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const date = new Date(value);
|
|
113
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
export const normalizeMultiSelectValueForStringArrayValues = (fieldConfig: FormFieldSchema, value: any): string[] => {
|
|
118
|
+
if (fieldConfig.outputFormat === OutputFormatType.STRING) {
|
|
119
|
+
return normalizeMultiSelectValue(value);
|
|
120
|
+
}
|
|
121
|
+
return value;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
export const normalizeFileFieldUrls = (value: any): string[] => {
|
|
126
|
+
if (value == null) return [];
|
|
127
|
+
|
|
128
|
+
if (Array.isArray(value)) {
|
|
129
|
+
return value.map((v) => String(v).trim()).filter(Boolean);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (typeof value === 'string') {
|
|
133
|
+
const s = value.trim();
|
|
134
|
+
if (!s || s === '[]') return [];
|
|
135
|
+
if (s.startsWith('[')) {
|
|
136
|
+
try {
|
|
137
|
+
const parsed = JSON.parse(s);
|
|
138
|
+
if (Array.isArray(parsed)) {
|
|
139
|
+
return parsed.map((v) => String(v).trim()).filter(Boolean);
|
|
140
|
+
}
|
|
141
|
+
} catch {
|
|
142
|
+
// fall through — treat as single URL
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return [s];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return [];
|
|
149
|
+
};
|