@bsol-oss/react-datatable5 12.0.0-beta.72 → 12.0.0-beta.74
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/README.md +67 -40
- package/dist/index.d.ts +15 -2
- package/dist/index.js +230 -155
- package/dist/index.mjs +230 -155
- package/dist/types/components/Form/SchemaFormContext.d.ts +4 -1
- package/dist/types/components/Form/components/core/FormRoot.d.ts +4 -2
- package/dist/types/components/Form/components/fields/DatePicker.d.ts +1 -1
- package/dist/types/components/Form/components/fields/EnumPicker.d.ts +1 -1
- package/dist/types/components/Form/components/fields/NumberInputField.d.ts +1 -1
- package/dist/types/components/Form/components/types/CustomJSONSchema7.d.ts +11 -0
- package/dist/types/components/Form/components/viewers/NumberViewer.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -3671,16 +3671,14 @@ const SchemaFormContext = createContext({
|
|
|
3671
3671
|
showResetButton: true,
|
|
3672
3672
|
showTitle: true,
|
|
3673
3673
|
},
|
|
3674
|
+
requireConfirmation: false,
|
|
3675
|
+
onFormSubmit: async () => { },
|
|
3674
3676
|
});
|
|
3675
3677
|
|
|
3676
3678
|
const useSchemaContext = () => {
|
|
3677
3679
|
return useContext(SchemaFormContext);
|
|
3678
3680
|
};
|
|
3679
3681
|
|
|
3680
|
-
const clearEmptyString = (object) => {
|
|
3681
|
-
return Object.fromEntries(Object.entries(object).filter(([, value]) => value !== ""));
|
|
3682
|
-
};
|
|
3683
|
-
|
|
3684
3682
|
const validateData = (data, schema) => {
|
|
3685
3683
|
const ajv = new Ajv({
|
|
3686
3684
|
strict: false,
|
|
@@ -3699,6 +3697,10 @@ const validateData = (data, schema) => {
|
|
|
3699
3697
|
};
|
|
3700
3698
|
};
|
|
3701
3699
|
|
|
3700
|
+
const clearEmptyString = (object) => {
|
|
3701
|
+
return Object.fromEntries(Object.entries(object).filter(([, value]) => value !== ""));
|
|
3702
|
+
};
|
|
3703
|
+
|
|
3702
3704
|
const idPickerSanityCheck = (column, foreign_key) => {
|
|
3703
3705
|
if (!!foreign_key == false) {
|
|
3704
3706
|
throw new Error(`The key foreign_key does not exist in properties of column ${column} when using id-picker.`);
|
|
@@ -3718,13 +3720,88 @@ const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, childre
|
|
|
3718
3720
|
showSubmitButton: true,
|
|
3719
3721
|
showResetButton: true,
|
|
3720
3722
|
showTitle: true,
|
|
3721
|
-
}, dateTimePickerLabels, idPickerLabels, }) => {
|
|
3723
|
+
}, requireConfirmation = false, dateTimePickerLabels, idPickerLabels, enumPickerLabels, }) => {
|
|
3722
3724
|
const [isSuccess, setIsSuccess] = useState(false);
|
|
3723
3725
|
const [isError, setIsError] = useState(false);
|
|
3724
3726
|
const [isSubmiting, setIsSubmiting] = useState(false);
|
|
3725
3727
|
const [isConfirming, setIsConfirming] = useState(false);
|
|
3726
3728
|
const [validatedData, setValidatedData] = useState();
|
|
3727
3729
|
const [error, setError] = useState();
|
|
3730
|
+
const onBeforeSubmit = () => {
|
|
3731
|
+
setIsSubmiting(true);
|
|
3732
|
+
};
|
|
3733
|
+
const onAfterSubmit = () => {
|
|
3734
|
+
setIsSubmiting(false);
|
|
3735
|
+
};
|
|
3736
|
+
const onSubmitError = (error) => {
|
|
3737
|
+
setIsError(true);
|
|
3738
|
+
setError(error);
|
|
3739
|
+
};
|
|
3740
|
+
const onSubmitSuccess = () => {
|
|
3741
|
+
setIsSuccess(true);
|
|
3742
|
+
};
|
|
3743
|
+
const validateFormData = (data) => {
|
|
3744
|
+
try {
|
|
3745
|
+
const { isValid, errors } = validateData(data, schema);
|
|
3746
|
+
return {
|
|
3747
|
+
isValid,
|
|
3748
|
+
errors,
|
|
3749
|
+
};
|
|
3750
|
+
}
|
|
3751
|
+
catch (error) {
|
|
3752
|
+
return {
|
|
3753
|
+
isValid: false,
|
|
3754
|
+
errors: [
|
|
3755
|
+
{
|
|
3756
|
+
field: 'validation',
|
|
3757
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
3758
|
+
},
|
|
3759
|
+
],
|
|
3760
|
+
};
|
|
3761
|
+
}
|
|
3762
|
+
};
|
|
3763
|
+
const defaultOnSubmit = async (promise) => {
|
|
3764
|
+
try {
|
|
3765
|
+
onBeforeSubmit();
|
|
3766
|
+
await promise;
|
|
3767
|
+
onSubmitSuccess();
|
|
3768
|
+
}
|
|
3769
|
+
catch (error) {
|
|
3770
|
+
onSubmitError(error);
|
|
3771
|
+
}
|
|
3772
|
+
finally {
|
|
3773
|
+
onAfterSubmit();
|
|
3774
|
+
}
|
|
3775
|
+
};
|
|
3776
|
+
const defaultSubmitPromise = (data) => {
|
|
3777
|
+
const options = {
|
|
3778
|
+
method: 'POST',
|
|
3779
|
+
url: `${requestUrl}`,
|
|
3780
|
+
data: clearEmptyString(data),
|
|
3781
|
+
...requestOptions,
|
|
3782
|
+
};
|
|
3783
|
+
return axios.request(options);
|
|
3784
|
+
};
|
|
3785
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3786
|
+
const onFormSubmit = async (data) => {
|
|
3787
|
+
// Validate data using AJV before submission
|
|
3788
|
+
const validationResult = validateFormData(data);
|
|
3789
|
+
if (!validationResult.isValid) {
|
|
3790
|
+
// Set validation errors
|
|
3791
|
+
const validationErrorMessage = {
|
|
3792
|
+
type: 'validation',
|
|
3793
|
+
errors: validationResult.errors,
|
|
3794
|
+
message: translate.t('validation_error'),
|
|
3795
|
+
};
|
|
3796
|
+
onSubmitError(validationErrorMessage);
|
|
3797
|
+
return;
|
|
3798
|
+
}
|
|
3799
|
+
if (onSubmit === undefined) {
|
|
3800
|
+
await defaultOnSubmit(defaultSubmitPromise(data));
|
|
3801
|
+
return;
|
|
3802
|
+
}
|
|
3803
|
+
await defaultOnSubmit(onSubmit(data));
|
|
3804
|
+
};
|
|
3728
3805
|
return (jsx(SchemaFormContext.Provider, { value: {
|
|
3729
3806
|
schema,
|
|
3730
3807
|
serverUrl,
|
|
@@ -3754,8 +3831,11 @@ const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, childre
|
|
|
3754
3831
|
customErrorRenderer,
|
|
3755
3832
|
customSuccessRenderer,
|
|
3756
3833
|
displayConfig,
|
|
3834
|
+
requireConfirmation,
|
|
3835
|
+
onFormSubmit,
|
|
3757
3836
|
dateTimePickerLabels,
|
|
3758
3837
|
idPickerLabels,
|
|
3838
|
+
enumPickerLabels,
|
|
3759
3839
|
}, children: jsx(FormProvider, { ...form, children: children }) }));
|
|
3760
3840
|
};
|
|
3761
3841
|
|
|
@@ -4014,14 +4094,16 @@ dayjs.extend(utc);
|
|
|
4014
4094
|
dayjs.extend(timezone);
|
|
4015
4095
|
const DatePicker = ({ column, schema, prefix }) => {
|
|
4016
4096
|
const { watch, formState: { errors }, setValue, } = useFormContext();
|
|
4017
|
-
const { timezone } = useSchemaContext();
|
|
4097
|
+
const { timezone, dateTimePickerLabels } = useSchemaContext();
|
|
4018
4098
|
const formI18n = useFormI18n(column, prefix);
|
|
4019
|
-
const { required, gridColumn =
|
|
4099
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1', displayDateFormat = 'YYYY-MM-DD', dateFormat = 'YYYY-MM-DD', } = schema;
|
|
4020
4100
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4021
4101
|
const colLabel = formI18n.colLabel;
|
|
4022
4102
|
const [open, setOpen] = useState(false);
|
|
4023
4103
|
const selectedDate = watch(colLabel);
|
|
4024
|
-
const displayDate = dayjs(selectedDate)
|
|
4104
|
+
const displayDate = dayjs(selectedDate)
|
|
4105
|
+
.tz(timezone)
|
|
4106
|
+
.format(displayDateFormat);
|
|
4025
4107
|
useEffect(() => {
|
|
4026
4108
|
try {
|
|
4027
4109
|
if (selectedDate) {
|
|
@@ -4045,45 +4127,83 @@ const DatePicker = ({ column, schema, prefix }) => {
|
|
|
4045
4127
|
console.error(e);
|
|
4046
4128
|
}
|
|
4047
4129
|
}, [selectedDate, dateFormat, colLabel, setValue]);
|
|
4048
|
-
return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems:
|
|
4130
|
+
return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
4049
4131
|
gridRow, children: [jsxs(PopoverRoot, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, children: [jsx(PopoverTrigger, { asChild: true, children: jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
|
|
4050
4132
|
setOpen(true);
|
|
4051
|
-
}, justifyContent:
|
|
4133
|
+
}, justifyContent: 'start', children: [jsx(MdDateRange, {}), selectedDate !== undefined ? `${displayDate}` : ''] }) }), jsx(PopoverContent, { children: jsxs(PopoverBody, { children: [jsx(PopoverTitle, {}), jsx(DatePicker$1, { selected: new Date(selectedDate), onDateSelected: ({ date }) => {
|
|
4052
4134
|
setValue(colLabel, dayjs(date).format(dateFormat));
|
|
4053
4135
|
setOpen(false);
|
|
4054
4136
|
}, labels: {
|
|
4055
|
-
monthNamesShort: [
|
|
4056
|
-
formI18n.translate.t(`common.month_1`, {
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
formI18n.translate.t(`common.
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
formI18n.translate.t(`common.
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
formI18n.translate.t(`common.
|
|
4066
|
-
|
|
4067
|
-
|
|
4137
|
+
monthNamesShort: dateTimePickerLabels?.monthNamesShort ?? [
|
|
4138
|
+
formI18n.translate.t(`common.month_1`, {
|
|
4139
|
+
defaultValue: 'January',
|
|
4140
|
+
}),
|
|
4141
|
+
formI18n.translate.t(`common.month_2`, {
|
|
4142
|
+
defaultValue: 'February',
|
|
4143
|
+
}),
|
|
4144
|
+
formI18n.translate.t(`common.month_3`, {
|
|
4145
|
+
defaultValue: 'March',
|
|
4146
|
+
}),
|
|
4147
|
+
formI18n.translate.t(`common.month_4`, {
|
|
4148
|
+
defaultValue: 'April',
|
|
4149
|
+
}),
|
|
4150
|
+
formI18n.translate.t(`common.month_5`, {
|
|
4151
|
+
defaultValue: 'May',
|
|
4152
|
+
}),
|
|
4153
|
+
formI18n.translate.t(`common.month_6`, {
|
|
4154
|
+
defaultValue: 'June',
|
|
4155
|
+
}),
|
|
4156
|
+
formI18n.translate.t(`common.month_7`, {
|
|
4157
|
+
defaultValue: 'July',
|
|
4158
|
+
}),
|
|
4159
|
+
formI18n.translate.t(`common.month_8`, {
|
|
4160
|
+
defaultValue: 'August',
|
|
4161
|
+
}),
|
|
4162
|
+
formI18n.translate.t(`common.month_9`, {
|
|
4163
|
+
defaultValue: 'September',
|
|
4164
|
+
}),
|
|
4165
|
+
formI18n.translate.t(`common.month_10`, {
|
|
4166
|
+
defaultValue: 'October',
|
|
4167
|
+
}),
|
|
4168
|
+
formI18n.translate.t(`common.month_11`, {
|
|
4169
|
+
defaultValue: 'November',
|
|
4170
|
+
}),
|
|
4171
|
+
formI18n.translate.t(`common.month_12`, {
|
|
4172
|
+
defaultValue: 'December',
|
|
4173
|
+
}),
|
|
4068
4174
|
],
|
|
4069
|
-
weekdayNamesShort: [
|
|
4070
|
-
formI18n.translate.t(`common.weekday_1`, {
|
|
4071
|
-
|
|
4072
|
-
|
|
4175
|
+
weekdayNamesShort: dateTimePickerLabels?.weekdayNamesShort ?? [
|
|
4176
|
+
formI18n.translate.t(`common.weekday_1`, {
|
|
4177
|
+
defaultValue: 'Sun',
|
|
4178
|
+
}),
|
|
4179
|
+
formI18n.translate.t(`common.weekday_2`, {
|
|
4180
|
+
defaultValue: 'Mon',
|
|
4181
|
+
}),
|
|
4182
|
+
formI18n.translate.t(`common.weekday_3`, {
|
|
4183
|
+
defaultValue: 'Tue',
|
|
4184
|
+
}),
|
|
4073
4185
|
formI18n.translate.t(`common.weekday_4`, {
|
|
4074
|
-
defaultValue:
|
|
4186
|
+
defaultValue: 'Wed',
|
|
4187
|
+
}),
|
|
4188
|
+
formI18n.translate.t(`common.weekday_5`, {
|
|
4189
|
+
defaultValue: 'Thu',
|
|
4190
|
+
}),
|
|
4191
|
+
formI18n.translate.t(`common.weekday_6`, {
|
|
4192
|
+
defaultValue: 'Fri',
|
|
4193
|
+
}),
|
|
4194
|
+
formI18n.translate.t(`common.weekday_7`, {
|
|
4195
|
+
defaultValue: 'Sat',
|
|
4075
4196
|
}),
|
|
4076
|
-
formI18n.translate.t(`common.weekday_5`, { defaultValue: "Thu" }),
|
|
4077
|
-
formI18n.translate.t(`common.weekday_6`, { defaultValue: "Fri" }),
|
|
4078
|
-
formI18n.translate.t(`common.weekday_7`, { defaultValue: "Sat" }),
|
|
4079
4197
|
],
|
|
4080
|
-
backButtonLabel:
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4198
|
+
backButtonLabel: dateTimePickerLabels?.backButtonLabel ??
|
|
4199
|
+
formI18n.translate.t(`common.back_button`, {
|
|
4200
|
+
defaultValue: 'Back',
|
|
4201
|
+
}),
|
|
4202
|
+
forwardButtonLabel: dateTimePickerLabels?.forwardButtonLabel ??
|
|
4203
|
+
formI18n.translate.t(`common.forward_button`, {
|
|
4204
|
+
defaultValue: 'Forward',
|
|
4205
|
+
}),
|
|
4206
|
+
} })] }) })] }), errors[`${column}`] && (jsx(Text, { color: 'red.400', children: formI18n.required() }))] }));
|
|
4087
4207
|
};
|
|
4088
4208
|
|
|
4089
4209
|
function filterArray(array, searchTerm) {
|
|
@@ -4098,10 +4218,10 @@ function filterArray(array, searchTerm) {
|
|
|
4098
4218
|
|
|
4099
4219
|
const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLimit = false, }) => {
|
|
4100
4220
|
const { watch, formState: { errors }, setValue, } = useFormContext();
|
|
4101
|
-
const { translate } = useSchemaContext();
|
|
4221
|
+
const { translate, enumPickerLabels } = useSchemaContext();
|
|
4102
4222
|
const { required, variant } = schema;
|
|
4103
4223
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4104
|
-
const { gridColumn =
|
|
4224
|
+
const { gridColumn = 'span 12', gridRow = 'span 1', renderDisplay } = schema;
|
|
4105
4225
|
const [searchText, setSearchText] = useState();
|
|
4106
4226
|
const [limit, setLimit] = useState(10);
|
|
4107
4227
|
const [openSearchResult, setOpenSearchResult] = useState();
|
|
@@ -4116,9 +4236,9 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
|
|
|
4116
4236
|
setSearchText(event.target.value);
|
|
4117
4237
|
setLimit(10);
|
|
4118
4238
|
};
|
|
4119
|
-
if (variant ===
|
|
4120
|
-
return (jsx(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, alignItems:
|
|
4121
|
-
gridRow, children: jsx(RadioGroup$1.Root, { defaultValue: "1", children: jsx(HStack, { gap: "6", children: filterArray(dataList, searchText ??
|
|
4239
|
+
if (variant === 'radio') {
|
|
4240
|
+
return (jsx(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, alignItems: 'stretch', gridColumn,
|
|
4241
|
+
gridRow, children: jsx(RadioGroup$1.Root, { defaultValue: "1", children: jsx(HStack, { gap: "6", children: filterArray(dataList, searchText ?? '').map((item) => {
|
|
4122
4242
|
return (jsxs(RadioGroup$1.Item, { onClick: () => {
|
|
4123
4243
|
if (!isMultiple) {
|
|
4124
4244
|
setOpenSearchResult(false);
|
|
@@ -4132,8 +4252,8 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
|
|
|
4132
4252
|
: translate.t(removeIndex(`${colLabel}.${item}`)) })] }, `${colLabel}-${item}`));
|
|
4133
4253
|
}) }) }) }));
|
|
4134
4254
|
}
|
|
4135
|
-
return (jsxs(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, alignItems:
|
|
4136
|
-
gridRow, children: [isMultiple && (jsxs(Flex, { flexFlow:
|
|
4255
|
+
return (jsxs(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, alignItems: 'stretch', gridColumn,
|
|
4256
|
+
gridRow, children: [isMultiple && (jsxs(Flex, { flexFlow: 'wrap', gap: 1, children: [watchEnums.map((enumValue) => {
|
|
4137
4257
|
const item = enumValue;
|
|
4138
4258
|
if (!!item === false) {
|
|
4139
4259
|
return jsx(Fragment, {});
|
|
@@ -4143,18 +4263,20 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
|
|
|
4143
4263
|
}, children: !!renderDisplay === true
|
|
4144
4264
|
? renderDisplay(item)
|
|
4145
4265
|
: translate.t(removeIndex(`${colLabel}.${item}`)) }, item));
|
|
4146
|
-
}), jsx(Tag, { size: "lg", cursor:
|
|
4266
|
+
}), jsx(Tag, { size: "lg", cursor: 'pointer', onClick: () => {
|
|
4147
4267
|
setOpenSearchResult(true);
|
|
4148
|
-
}, children:
|
|
4268
|
+
}, children: enumPickerLabels?.addMore ??
|
|
4269
|
+
translate.t(removeIndex(`${colLabel}.add_more`)) }, `${colLabel}-add-more-tag`)] })), !isMultiple && (jsx(Button, { variant: 'outline', onClick: () => {
|
|
4149
4270
|
setOpenSearchResult(true);
|
|
4150
|
-
}, justifyContent:
|
|
4151
|
-
?
|
|
4152
|
-
: translate.t(removeIndex(`${colLabel}.${watchEnum ??
|
|
4271
|
+
}, justifyContent: 'start', children: !!watchEnum === false
|
|
4272
|
+
? ''
|
|
4273
|
+
: translate.t(removeIndex(`${colLabel}.${watchEnum ?? 'null'}`)) })), jsxs(PopoverRoot, { open: openSearchResult, onOpenChange: (e) => setOpenSearchResult(e.open), closeOnInteractOutside: true, initialFocusEl: () => ref.current, positioning: { placement: 'bottom-start' }, children: [jsx(PopoverTrigger, {}), jsx(PopoverContent, { portalled: false, children: jsxs(PopoverBody, { display: 'grid', gap: 1, children: [jsx(Input, { placeholder: enumPickerLabels?.typeToSearch ??
|
|
4274
|
+
translate.t(`${colLabel}.type_to_search`), onChange: (event) => {
|
|
4153
4275
|
onSearchChange(event);
|
|
4154
4276
|
setOpenSearchResult(true);
|
|
4155
|
-
}, autoComplete: "off", ref: ref }), jsx(PopoverTitle, {}), showTotalAndLimit && (jsx(Text, { children: `${translate.t(removeIndex(`${colLabel}.total`))}: ${count}, ${translate.t(removeIndex(`${colLabel}.showing`))} ${limit}` })), jsxs(Grid, { overflow:
|
|
4277
|
+
}, autoComplete: "off", ref: ref }), jsx(PopoverTitle, {}), showTotalAndLimit && (jsx(Text, { children: `${enumPickerLabels?.total ?? translate.t(removeIndex(`${colLabel}.total`))}: ${count}, ${enumPickerLabels?.showing ?? translate.t(removeIndex(`${colLabel}.showing`))} ${limit}` })), jsxs(Grid, { overflow: 'auto', maxHeight: '20rem', children: [jsx(Flex, { flexFlow: 'column wrap', children: dataList
|
|
4156
4278
|
.filter((item) => {
|
|
4157
|
-
const searchTerm = (searchText ||
|
|
4279
|
+
const searchTerm = (searchText || '').toLowerCase();
|
|
4158
4280
|
if (!searchTerm)
|
|
4159
4281
|
return true;
|
|
4160
4282
|
// Check if the original enum value contains the search text
|
|
@@ -4174,7 +4296,7 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
|
|
|
4174
4296
|
const selected = isMultiple
|
|
4175
4297
|
? watchEnums.some((enumValue) => item === enumValue)
|
|
4176
4298
|
: watchEnum == item;
|
|
4177
|
-
return (jsx(Box, { cursor:
|
|
4299
|
+
return (jsx(Box, { cursor: 'pointer', onClick: () => {
|
|
4178
4300
|
if (!isMultiple) {
|
|
4179
4301
|
setOpenSearchResult(false);
|
|
4180
4302
|
setValue(colLabel, item);
|
|
@@ -4182,10 +4304,11 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
|
|
|
4182
4304
|
}
|
|
4183
4305
|
const newSet = new Set([...(watchEnums ?? []), item]);
|
|
4184
4306
|
setValue(colLabel, [...newSet]);
|
|
4185
|
-
}, ...(selected ? { color:
|
|
4307
|
+
}, ...(selected ? { color: 'colorPalette.400/50' } : {}), children: !!renderDisplay === true
|
|
4186
4308
|
? renderDisplay(item)
|
|
4187
4309
|
: translate.t(removeIndex(`${colLabel}.${item}`)) }, `${colLabel}-${item}`));
|
|
4188
|
-
}) }), isDirty && (jsx(Fragment, { children: dataList.length <= 0 && (jsx(Fragment, { children:
|
|
4310
|
+
}) }), isDirty && (jsx(Fragment, { children: dataList.length <= 0 && (jsx(Fragment, { children: enumPickerLabels?.emptySearchResult ??
|
|
4311
|
+
translate.t(removeIndex(`${colLabel}.empty_search_result`)) })) }))] })] }) })] }), errors[`${colLabel}`] && (jsx(Text, { color: 'red.400', children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
|
|
4189
4312
|
};
|
|
4190
4313
|
|
|
4191
4314
|
function isEnteringWindow(_ref) {
|
|
@@ -4810,13 +4933,13 @@ NumberInput.Label;
|
|
|
4810
4933
|
const NumberInputField = ({ schema, column, prefix, }) => {
|
|
4811
4934
|
const { setValue, formState: { errors }, watch, } = useFormContext();
|
|
4812
4935
|
const { translate } = useSchemaContext();
|
|
4813
|
-
const { required, gridColumn =
|
|
4936
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1' } = schema;
|
|
4814
4937
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4815
4938
|
const colLabel = `${prefix}${column}`;
|
|
4816
4939
|
const value = watch(`${colLabel}`);
|
|
4817
|
-
return (jsxs(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, gridColumn, gridRow, children: [jsx(NumberInputRoot, {
|
|
4818
|
-
|
|
4819
|
-
|
|
4940
|
+
return (jsxs(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, gridColumn, gridRow, children: [jsx(NumberInputRoot, { value: value, onValueChange: (details) => {
|
|
4941
|
+
setValue(`${colLabel}`, details.value); // Store as string to avoid floating-point precision issues
|
|
4942
|
+
}, min: schema.minimum, max: schema.maximum, step: schema.multipleOf || 0.01, allowOverflow: false, clampValueOnBlur: false, inputMode: "decimal", formatOptions: schema.formatOptions, children: jsx(NumberInputField$1, { required: isRequired }) }), errors[`${column}`] && (jsx(Text, { color: 'red.400', children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
|
|
4820
4943
|
};
|
|
4821
4944
|
|
|
4822
4945
|
const ObjectInput = ({ schema, column, prefix }) => {
|
|
@@ -5758,11 +5881,29 @@ const IdViewer = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
5758
5881
|
const NumberViewer = ({ schema, column, prefix, }) => {
|
|
5759
5882
|
const { watch, formState: { errors }, } = useFormContext();
|
|
5760
5883
|
const { translate } = useSchemaContext();
|
|
5761
|
-
const { required, gridColumn =
|
|
5884
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1' } = schema;
|
|
5762
5885
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
5763
5886
|
const colLabel = `${prefix}${column}`;
|
|
5764
5887
|
const value = watch(colLabel);
|
|
5765
|
-
|
|
5888
|
+
// Format the value for display if formatOptions are provided
|
|
5889
|
+
const formatValue = (val) => {
|
|
5890
|
+
if (val === undefined || val === null || val === '')
|
|
5891
|
+
return '';
|
|
5892
|
+
const numValue = typeof val === 'string' ? parseFloat(val) : val;
|
|
5893
|
+
if (isNaN(numValue))
|
|
5894
|
+
return String(val);
|
|
5895
|
+
// Use formatOptions if available, otherwise display as-is
|
|
5896
|
+
if (schema.formatOptions) {
|
|
5897
|
+
try {
|
|
5898
|
+
return new Intl.NumberFormat(undefined, schema.formatOptions).format(numValue);
|
|
5899
|
+
}
|
|
5900
|
+
catch {
|
|
5901
|
+
return String(val);
|
|
5902
|
+
}
|
|
5903
|
+
}
|
|
5904
|
+
return String(val);
|
|
5905
|
+
};
|
|
5906
|
+
return (jsxs(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, gridColumn, gridRow, children: [jsx(Text, { children: formatValue(value) }), errors[`${column}`] && (jsx(Text, { color: 'red.400', children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
|
|
5766
5907
|
};
|
|
5767
5908
|
|
|
5768
5909
|
const ObjectViewer = ({ schema, column, prefix }) => {
|
|
@@ -6054,112 +6195,46 @@ const ColumnViewer = ({ column, properties, prefix, }) => {
|
|
|
6054
6195
|
};
|
|
6055
6196
|
|
|
6056
6197
|
const SubmitButton = () => {
|
|
6057
|
-
const { translate, setValidatedData, setIsError, setIsConfirming, setError, schema, } = useSchemaContext();
|
|
6198
|
+
const { translate, setValidatedData, setIsError, setIsConfirming, setError, schema, requireConfirmation, onFormSubmit, } = useSchemaContext();
|
|
6058
6199
|
const methods = useFormContext();
|
|
6059
6200
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6060
6201
|
const onValid = (data) => {
|
|
6061
6202
|
const { isValid, errors } = validateData(data, schema);
|
|
6062
6203
|
if (!isValid) {
|
|
6063
6204
|
setError({
|
|
6064
|
-
type:
|
|
6205
|
+
type: 'validation',
|
|
6065
6206
|
errors,
|
|
6066
6207
|
});
|
|
6067
6208
|
setIsError(true);
|
|
6068
6209
|
return;
|
|
6069
6210
|
}
|
|
6070
|
-
// If validation passes,
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6211
|
+
// If validation passes, check if confirmation is required
|
|
6212
|
+
if (requireConfirmation) {
|
|
6213
|
+
// Show confirmation (existing behavior)
|
|
6214
|
+
setValidatedData(data);
|
|
6215
|
+
setIsError(false);
|
|
6216
|
+
setIsConfirming(true);
|
|
6217
|
+
}
|
|
6218
|
+
else {
|
|
6219
|
+
// Skip confirmation and submit directly
|
|
6220
|
+
setValidatedData(data);
|
|
6221
|
+
setIsError(false);
|
|
6222
|
+
onFormSubmit(data);
|
|
6223
|
+
}
|
|
6074
6224
|
};
|
|
6075
6225
|
return (jsx(Button$1, { onClick: () => {
|
|
6076
6226
|
methods.handleSubmit(onValid)();
|
|
6077
|
-
}, formNoValidate: true, children: translate.t(
|
|
6227
|
+
}, formNoValidate: true, children: translate.t('submit') }));
|
|
6078
6228
|
};
|
|
6079
6229
|
|
|
6080
6230
|
const FormBody = () => {
|
|
6081
|
-
const { schema,
|
|
6231
|
+
const { schema, order, ignore, include, translate, isSuccess, setIsSuccess, isError, setIsError, isSubmiting, setIsSubmiting, isConfirming, setIsConfirming, validatedData, setValidatedData, error, getUpdatedData, customErrorRenderer, customSuccessRenderer, displayConfig, onFormSubmit, } = useSchemaContext();
|
|
6082
6232
|
const { showSubmitButton, showResetButton } = displayConfig;
|
|
6083
6233
|
const methods = useFormContext();
|
|
6084
6234
|
const { properties } = schema;
|
|
6085
|
-
const onBeforeSubmit = () => {
|
|
6086
|
-
setIsSubmiting(true);
|
|
6087
|
-
};
|
|
6088
|
-
const onAfterSubmit = () => {
|
|
6089
|
-
setIsSubmiting(false);
|
|
6090
|
-
};
|
|
6091
|
-
const onSubmitError = (error) => {
|
|
6092
|
-
setIsError(true);
|
|
6093
|
-
setError(error);
|
|
6094
|
-
};
|
|
6095
|
-
const onSubmitSuccess = () => {
|
|
6096
|
-
setIsSuccess(true);
|
|
6097
|
-
};
|
|
6098
|
-
const validateFormData = (data) => {
|
|
6099
|
-
try {
|
|
6100
|
-
const { isValid, errors } = validateData(data, schema);
|
|
6101
|
-
return {
|
|
6102
|
-
isValid,
|
|
6103
|
-
errors,
|
|
6104
|
-
};
|
|
6105
|
-
}
|
|
6106
|
-
catch (error) {
|
|
6107
|
-
return {
|
|
6108
|
-
isValid: false,
|
|
6109
|
-
errors: [
|
|
6110
|
-
{
|
|
6111
|
-
field: "validation",
|
|
6112
|
-
message: error instanceof Error ? error.message : "Unknown error",
|
|
6113
|
-
},
|
|
6114
|
-
],
|
|
6115
|
-
};
|
|
6116
|
-
}
|
|
6117
|
-
};
|
|
6118
|
-
const defaultOnSubmit = async (promise) => {
|
|
6119
|
-
try {
|
|
6120
|
-
onBeforeSubmit();
|
|
6121
|
-
await promise;
|
|
6122
|
-
onSubmitSuccess();
|
|
6123
|
-
}
|
|
6124
|
-
catch (error) {
|
|
6125
|
-
onSubmitError(error);
|
|
6126
|
-
}
|
|
6127
|
-
finally {
|
|
6128
|
-
onAfterSubmit();
|
|
6129
|
-
}
|
|
6130
|
-
};
|
|
6131
|
-
const defaultSubmitPromise = (data) => {
|
|
6132
|
-
const options = {
|
|
6133
|
-
method: "POST",
|
|
6134
|
-
url: `${requestUrl}`,
|
|
6135
|
-
data: clearEmptyString(data),
|
|
6136
|
-
...requestOptions,
|
|
6137
|
-
};
|
|
6138
|
-
return axios.request(options);
|
|
6139
|
-
};
|
|
6140
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6141
|
-
const onFormSubmit = async (data) => {
|
|
6142
|
-
// Validate data using AJV before submission
|
|
6143
|
-
const validationResult = validateFormData(data);
|
|
6144
|
-
if (!validationResult.isValid) {
|
|
6145
|
-
// Set validation errors
|
|
6146
|
-
const validationErrorMessage = {
|
|
6147
|
-
type: "validation",
|
|
6148
|
-
errors: validationResult.errors,
|
|
6149
|
-
message: translate.t("validation_error"),
|
|
6150
|
-
};
|
|
6151
|
-
onSubmitError(validationErrorMessage);
|
|
6152
|
-
return;
|
|
6153
|
-
}
|
|
6154
|
-
if (onSubmit === undefined) {
|
|
6155
|
-
await defaultOnSubmit(defaultSubmitPromise(data));
|
|
6156
|
-
return;
|
|
6157
|
-
}
|
|
6158
|
-
await defaultOnSubmit(onSubmit(data));
|
|
6159
|
-
};
|
|
6160
6235
|
// Custom error renderer for validation errors with i18n support
|
|
6161
6236
|
const renderValidationErrors = (validationErrors) => {
|
|
6162
|
-
return (jsx(Flex, { flexFlow:
|
|
6237
|
+
return (jsx(Flex, { flexFlow: 'column', gap: "2", children: validationErrors.map((err, index) => (jsxs(Alert.Root, { status: "error", display: "flex", alignItems: "center", children: [jsx(Alert.Indicator, {}), jsx(Alert.Content, { children: jsx(Alert.Description, { children: err.message }) })] }, index))) }));
|
|
6163
6238
|
};
|
|
6164
6239
|
const renderColumns = ({ order, keys, ignore, include, }) => {
|
|
6165
6240
|
const included = include.length > 0 ? include : keys;
|
|
@@ -6187,32 +6262,32 @@ const FormBody = () => {
|
|
|
6187
6262
|
if (customSuccessRenderer) {
|
|
6188
6263
|
return customSuccessRenderer(resetHandler);
|
|
6189
6264
|
}
|
|
6190
|
-
return (jsxs(Flex, { flexFlow:
|
|
6265
|
+
return (jsxs(Flex, { flexFlow: 'column', gap: "2", children: [jsxs(Alert.Root, { status: "success", children: [jsx(Alert.Indicator, {}), jsx(Alert.Content, { children: jsx(Alert.Title, { children: translate.t('submit_success') }) })] }), jsx(Flex, { justifyContent: 'end', children: jsx(Button$1, { onClick: resetHandler, formNoValidate: true, children: translate.t('submit_again') }) })] }));
|
|
6191
6266
|
}
|
|
6192
6267
|
if (isConfirming) {
|
|
6193
|
-
return (jsxs(Flex, { flexFlow:
|
|
6268
|
+
return (jsxs(Flex, { flexFlow: 'column', gap: "2", children: [jsx(Grid, { gap: 4, gridTemplateColumns: 'repeat(12, 1fr)', gridTemplateRows: 'repeat(12, max-content)', autoFlow: 'row', children: ordered.map((column) => {
|
|
6194
6269
|
return (jsx(ColumnViewer
|
|
6195
6270
|
// @ts-expect-error find suitable types
|
|
6196
6271
|
, {
|
|
6197
6272
|
// @ts-expect-error find suitable types
|
|
6198
6273
|
properties: properties, prefix: ``, column }, `form-viewer-${column}`));
|
|
6199
|
-
}) }), jsxs(Flex, { justifyContent:
|
|
6274
|
+
}) }), jsxs(Flex, { justifyContent: 'end', gap: '2', children: [jsx(Button$1, { onClick: () => {
|
|
6200
6275
|
setIsConfirming(false);
|
|
6201
|
-
}, variant:
|
|
6276
|
+
}, variant: 'subtle', children: translate.t('cancel') }), jsx(Button$1, { onClick: () => {
|
|
6202
6277
|
onFormSubmit(validatedData);
|
|
6203
|
-
}, children: translate.t(
|
|
6204
|
-
error?.errors ? (renderValidationErrors(error.errors)) : (jsxs(Alert.Root, { status: "error", children: [jsx(Alert.Indicator, {}), jsxs(Alert.Content, { children: [jsx(Alert.Title, { children: "Error" }), jsx(Alert.Description, { children: jsx(AccordionRoot, { collapsible: true, defaultValue: [], children: jsxs(AccordionItem, { value:
|
|
6278
|
+
}, children: translate.t('confirm') })] }), isSubmiting && (jsx(Box, { pos: "absolute", inset: "0", bg: "bg/80", children: jsx(Center, { h: "full", children: jsx(Spinner, { color: "teal.500" }) }) })), isError && (jsx(Fragment, { children: customErrorRenderer ? (customErrorRenderer(error)) : (jsx(Fragment, { children: error?.type === 'validation' &&
|
|
6279
|
+
error?.errors ? (renderValidationErrors(error.errors)) : (jsxs(Alert.Root, { status: "error", children: [jsx(Alert.Indicator, {}), jsxs(Alert.Content, { children: [jsx(Alert.Title, { children: "Error" }), jsx(Alert.Description, { children: jsx(AccordionRoot, { collapsible: true, defaultValue: [], children: jsxs(AccordionItem, { value: 'b', children: [jsx(AccordionItemTrigger, { children: `${error}` }), jsx(AccordionItemContent, { children: `${JSON.stringify(error)}` })] }) }) })] })] })) })) }))] }));
|
|
6205
6280
|
}
|
|
6206
|
-
return (jsxs(Flex, { flexFlow:
|
|
6281
|
+
return (jsxs(Flex, { flexFlow: 'column', gap: "2", children: [jsx(Grid, { gap: "4", gridTemplateColumns: 'repeat(12, 1fr)', autoFlow: 'row', children: ordered.map((column) => {
|
|
6207
6282
|
return (jsx(ColumnRenderer
|
|
6208
6283
|
// @ts-expect-error find suitable types
|
|
6209
6284
|
, {
|
|
6210
6285
|
// @ts-expect-error find suitable types
|
|
6211
6286
|
properties: properties, prefix: ``, column }, `form-input-${column}`));
|
|
6212
|
-
}) }), jsxs(Flex, { justifyContent:
|
|
6287
|
+
}) }), jsxs(Flex, { justifyContent: 'end', gap: "2", children: [showResetButton && (jsx(Button$1, { onClick: () => {
|
|
6213
6288
|
methods.reset();
|
|
6214
|
-
}, variant:
|
|
6215
|
-
error?.errors ? (renderValidationErrors(error.errors)) : (jsxs(Alert.Root, { status: "error", children: [jsx(Alert.Indicator, {}), jsxs(Alert.Content, { children: [jsx(Alert.Title, { children: "Error" }), jsx(Alert.Description, { children: jsx(AccordionRoot, { collapsible: true, defaultValue: [], children: jsxs(AccordionItem, { value:
|
|
6289
|
+
}, variant: 'subtle', children: translate.t('reset') })), showSubmitButton && jsx(SubmitButton, {})] }), isError && (jsx(Fragment, { children: customErrorRenderer ? (customErrorRenderer(error)) : (jsx(Fragment, { children: error?.type === 'validation' &&
|
|
6290
|
+
error?.errors ? (renderValidationErrors(error.errors)) : (jsxs(Alert.Root, { status: "error", children: [jsx(Alert.Indicator, {}), jsxs(Alert.Content, { children: [jsx(Alert.Title, { children: "Error" }), jsx(Alert.Description, { children: jsx(AccordionRoot, { collapsible: true, defaultValue: [], children: jsxs(AccordionItem, { value: 'b', children: [jsx(AccordionItemTrigger, { children: `${error}` }), jsx(AccordionItemContent, { children: `${JSON.stringify(error)}` })] }) }) })] })] })) })) }))] }));
|
|
6216
6291
|
};
|
|
6217
6292
|
|
|
6218
6293
|
const FormTitle = () => {
|
|
@@ -3,7 +3,7 @@ import { JSONSchema7 } from 'json-schema';
|
|
|
3
3
|
import { Dispatch, ReactNode, SetStateAction } from 'react';
|
|
4
4
|
import { FieldValues } from 'react-hook-form';
|
|
5
5
|
import { UseTranslationResponse } from 'react-i18next';
|
|
6
|
-
import { DateTimePickerLabels, IdPickerLabels } from './components/types/CustomJSONSchema7';
|
|
6
|
+
import { DateTimePickerLabels, IdPickerLabels, EnumPickerLabels } from './components/types/CustomJSONSchema7';
|
|
7
7
|
export interface SchemaFormContext<TData extends FieldValues> {
|
|
8
8
|
schema: JSONSchema7;
|
|
9
9
|
serverUrl: string;
|
|
@@ -38,7 +38,10 @@ export interface SchemaFormContext<TData extends FieldValues> {
|
|
|
38
38
|
showResetButton?: boolean;
|
|
39
39
|
showTitle?: boolean;
|
|
40
40
|
};
|
|
41
|
+
requireConfirmation: boolean;
|
|
42
|
+
onFormSubmit: (data: TData) => Promise<void>;
|
|
41
43
|
dateTimePickerLabels?: DateTimePickerLabels;
|
|
42
44
|
idPickerLabels?: IdPickerLabels;
|
|
45
|
+
enumPickerLabels?: EnumPickerLabels;
|
|
43
46
|
}
|
|
44
47
|
export declare const SchemaFormContext: import("react").Context<SchemaFormContext<unknown>>;
|
|
@@ -4,7 +4,7 @@ import { JSONSchema7 } from 'json-schema';
|
|
|
4
4
|
import { Dispatch, ReactNode, SetStateAction } from 'react';
|
|
5
5
|
import { FieldValues, SubmitHandler, UseFormReturn } from 'react-hook-form';
|
|
6
6
|
import { UseTranslationResponse } from 'react-i18next';
|
|
7
|
-
import { CustomJSONSchema7, DateTimePickerLabels, IdPickerLabels } from '../types/CustomJSONSchema7';
|
|
7
|
+
import { CustomJSONSchema7, DateTimePickerLabels, IdPickerLabels, EnumPickerLabels } from '../types/CustomJSONSchema7';
|
|
8
8
|
export interface FormRootProps<TData extends FieldValues> {
|
|
9
9
|
schema: CustomJSONSchema7;
|
|
10
10
|
serverUrl: string;
|
|
@@ -28,8 +28,10 @@ export interface FormRootProps<TData extends FieldValues> {
|
|
|
28
28
|
showResetButton?: boolean;
|
|
29
29
|
showTitle?: boolean;
|
|
30
30
|
};
|
|
31
|
+
requireConfirmation?: boolean;
|
|
31
32
|
dateTimePickerLabels?: DateTimePickerLabels;
|
|
32
33
|
idPickerLabels?: IdPickerLabels;
|
|
34
|
+
enumPickerLabels?: EnumPickerLabels;
|
|
33
35
|
}
|
|
34
36
|
export interface CustomJSONSchema7Definition extends JSONSchema7 {
|
|
35
37
|
variant: string;
|
|
@@ -46,4 +48,4 @@ export declare const idPickerSanityCheck: (column: string, foreign_key?: {
|
|
|
46
48
|
column?: string | undefined;
|
|
47
49
|
display_column?: string | undefined;
|
|
48
50
|
} | undefined) => void;
|
|
49
|
-
export declare const FormRoot: <TData extends FieldValues>({ schema, idMap, setIdMap, form, serverUrl, translate, children, order, ignore, include, onSubmit, rowNumber, requestOptions, getUpdatedData, customErrorRenderer, customSuccessRenderer, displayConfig, dateTimePickerLabels, idPickerLabels, }: FormRootProps<TData>) => import("react/jsx-runtime").JSX.Element;
|
|
51
|
+
export declare const FormRoot: <TData extends FieldValues>({ schema, idMap, setIdMap, form, serverUrl, translate, children, order, ignore, include, onSubmit, rowNumber, requestOptions, getUpdatedData, customErrorRenderer, customSuccessRenderer, displayConfig, requireConfirmation, dateTimePickerLabels, idPickerLabels, enumPickerLabels, }: FormRootProps<TData>) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { InputDefaultProps } from
|
|
1
|
+
import { InputDefaultProps } from './types';
|
|
2
2
|
export declare const DatePicker: ({ column, schema, prefix }: InputDefaultProps) => import("react/jsx-runtime").JSX.Element;
|