@bsol-oss/react-datatable5 12.0.0-beta.74 → 12.0.0-beta.76
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/dist/index.d.ts +159 -126
- package/dist/index.js +820 -279
- package/dist/index.mjs +822 -282
- package/dist/types/components/Form/SchemaFormContext.d.ts +5 -3
- package/dist/types/components/Form/components/core/FormRoot.d.ts +3 -2
- package/dist/types/components/Form/components/fields/BooleanPicker.d.ts +1 -1
- package/dist/types/components/Form/components/fields/ColumnRenderer.d.ts +3 -2
- package/dist/types/components/Form/components/fields/DateRangePicker.d.ts +2 -0
- package/dist/types/components/Form/components/fields/DateTimePicker.d.ts +1 -1
- package/dist/types/components/Form/components/fields/FilePicker.d.ts +1 -1
- package/dist/types/components/Form/components/fields/ObjectInput.d.ts +1 -1
- package/dist/types/components/Form/components/fields/RecordInput.d.ts +1 -1
- package/dist/types/components/Form/components/fields/SchemaRenderer.d.ts +1 -1
- package/dist/types/components/Form/components/fields/StringInputField.d.ts +1 -1
- package/dist/types/components/Form/components/fields/TextAreaInput.d.ts +1 -1
- package/dist/types/components/Form/components/fields/TimePicker.d.ts +1 -1
- package/dist/types/components/Form/components/types/CustomJSONSchema7.d.ts +28 -0
- package/dist/types/components/Form/useForm.d.ts +4 -2
- package/dist/types/components/Form/utils/ajvResolver.d.ts +13 -0
- package/dist/types/components/Form/utils/buildErrorMessages.d.ts +5 -1
- package/dist/types/components/Form/utils/formatBytes.d.ts +6 -0
- package/dist/types/components/Form/utils/getFieldError.d.ts +6 -0
- package/dist/types/components/Form/utils/useFormI18n.d.ts +1 -1
- package/dist/types/components/Form/utils/validateData.d.ts +2 -2
- package/dist/types/components/ui/field.d.ts +3 -3
- package/package.json +1 -1
- package/dist/types/components/Controls/DensityFeature.d.ts +0 -23
- package/dist/types/components/Controls/DensityToggleButton.d.ts +0 -6
- package/dist/types/components/Controls/EditFilterButton.d.ts +0 -9
- package/dist/types/components/Controls/EditOrderButton.d.ts +0 -7
- package/dist/types/components/Controls/EditSortingButton.d.ts +0 -7
- package/dist/types/components/Controls/EditViewButton.d.ts +0 -7
- package/dist/types/components/Controls/FilterDialog.d.ts +0 -5
- package/dist/types/components/Controls/PageSizeControl.d.ts +0 -4
- package/dist/types/components/Controls/Pagination.d.ts +0 -1
- package/dist/types/components/Controls/ResetFilteringButton.d.ts +0 -4
- package/dist/types/components/Controls/ResetSelectionButton.d.ts +0 -4
- package/dist/types/components/Controls/ResetSortingButton.d.ts +0 -4
- package/dist/types/components/Controls/RowCountText.d.ts +0 -1
- package/dist/types/components/Controls/SelectAllRowsToggle.d.ts +0 -8
- package/dist/types/components/Controls/TablePagination.d.ts +0 -1
- package/dist/types/components/Controls/ViewDialog.d.ts +0 -5
- package/dist/types/components/DataTable/CardHeader.d.ts +0 -13
- package/dist/types/components/DataTable/DataDisplay.d.ts +0 -6
- package/dist/types/components/DataTable/ReloadButton.d.ts +0 -5
- package/dist/types/components/DataTable/Table.d.ts +0 -10
- package/dist/types/components/DataTable/TableBody.d.ts +0 -21
- package/dist/types/components/DataTable/TableCardContainer.d.ts +0 -7
- package/dist/types/components/DataTable/TableCards.d.ts +0 -11
- package/dist/types/components/DataTable/TableComponent.d.ts +0 -6
- package/dist/types/components/DataTable/TableControls.d.ts +0 -21
- package/dist/types/components/DataTable/TableFilter.d.ts +0 -1
- package/dist/types/components/DataTable/TableFilterTags.d.ts +0 -1
- package/dist/types/components/DataTable/TableFilters.d.ts +0 -1
- package/dist/types/components/DataTable/TableFooter.d.ts +0 -9
- package/dist/types/components/DataTable/TableHeader.d.ts +0 -13
- package/dist/types/components/DataTable/TableLoadingComponent.d.ts +0 -5
- package/dist/types/components/DataTable/TableOrderer.d.ts +0 -1
- package/dist/types/components/DataTable/TableSelector.d.ts +0 -1
- package/dist/types/components/DataTable/TableSorter.d.ts +0 -1
- package/dist/types/components/DataTable/TableViewer.d.ts +0 -1
- package/dist/types/components/DataTable/TextCell.d.ts +0 -10
- package/dist/types/components/DataTable/components/EmptyState.d.ts +0 -5
- package/dist/types/components/DataTable/components/ErrorAlert.d.ts +0 -4
- package/dist/types/components/DataTable/components/RecordDisplay.d.ts +0 -9
- package/dist/types/components/DataTable/components/TextCell.d.ts +0 -10
- package/dist/types/components/Filter/DateRangeFilter.d.ts +0 -9
- package/dist/types/components/Filter/FilterOptions.d.ts +0 -4
- package/dist/types/components/Form/Form.d.ts +0 -36
- package/dist/types/components/Form/components/ArrayRenderer.d.ts +0 -7
- package/dist/types/components/Form/components/BooleanPicker.d.ts +0 -7
- package/dist/types/components/Form/components/ColumnRenderer.d.ts +0 -7
- package/dist/types/components/Form/components/DatePicker.d.ts +0 -7
- package/dist/types/components/Form/components/EnumPicker.d.ts +0 -8
- package/dist/types/components/Form/components/FilePicker.d.ts +0 -5
- package/dist/types/components/Form/components/IdPicker.d.ts +0 -8
- package/dist/types/components/Form/components/IdViewer.d.ts +0 -5
- package/dist/types/components/Form/components/NumberInputField.d.ts +0 -7
- package/dist/types/components/Form/components/ObjectInput.d.ts +0 -7
- package/dist/types/components/Form/components/RecordInput.d.ts +0 -7
- package/dist/types/components/Form/components/SchemaRenderer.d.ts +0 -7
- package/dist/types/components/Form/components/StringInputField.d.ts +0 -20
- package/dist/types/components/Form/components/TagPicker.d.ts +0 -30
- package/dist/types/components/Form/utils/translateWrapper.d.ts +0 -6
- package/dist/types/components/Form/utils/validation.d.ts +0 -104
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
-
import { Button as Button$1, AbsoluteCenter, Spinner, Span, IconButton, Portal, Dialog, Flex, Text, useDisclosure, DialogBackdrop, RadioGroup as RadioGroup$1, Grid, Box, Slider as Slider$1, HStack, For, Tag as Tag$1, Input, Menu, createRecipeContext, createContext as createContext$1, Pagination as Pagination$1, usePaginationContext, CheckboxCard as CheckboxCard$1, Image, EmptyState as EmptyState$2, VStack, Alert, Card, Group, InputElement, Tooltip as Tooltip$1, Icon, List, Table as Table$1, Checkbox as Checkbox$1, MenuRoot as MenuRoot$1, MenuTrigger as MenuTrigger$1,
|
|
2
|
+
import { Button as Button$1, AbsoluteCenter, Spinner, Span, IconButton, Portal, Dialog, Flex, Text, useDisclosure, DialogBackdrop, RadioGroup as RadioGroup$1, Grid, Box, Slider as Slider$1, HStack, For, Tag as Tag$1, Input, Menu, createRecipeContext, createContext as createContext$1, Pagination as Pagination$1, usePaginationContext, CheckboxCard as CheckboxCard$1, Image, EmptyState as EmptyState$2, VStack, Alert, Card, Group, InputElement, Tooltip as Tooltip$1, Icon, List, Table as Table$1, Checkbox as Checkbox$1, MenuRoot as MenuRoot$1, MenuTrigger as MenuTrigger$1, Field as Field$1, Popover, NumberInput, Show, RadioCard, CheckboxGroup, Center, Heading } from '@chakra-ui/react';
|
|
3
3
|
import { AiOutlineColumnWidth } from 'react-icons/ai';
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import React__default, { createContext, useContext, useState, useEffect, useRef, forwardRef } from 'react';
|
|
6
|
-
import { LuX, LuCheck, LuChevronRight,
|
|
6
|
+
import { LuX, LuCheck, LuChevronRight, LuFile, LuSearch } from 'react-icons/lu';
|
|
7
7
|
import { MdOutlineSort, MdFilterAlt, MdSearch, MdOutlineViewColumn, MdFilterListAlt, MdPushPin, MdCancel, MdClear, MdOutlineChecklist, MdDateRange } from 'react-icons/md';
|
|
8
8
|
import { FaUpDown, FaGripLinesVertical, FaTrash } from 'react-icons/fa6';
|
|
9
9
|
import { BiDownArrow, BiUpArrow, BiError } from 'react-icons/bi';
|
|
@@ -30,7 +30,6 @@ import axios from 'axios';
|
|
|
30
30
|
import { FormProvider, useFormContext, useForm as useForm$1 } from 'react-hook-form';
|
|
31
31
|
import Ajv from 'ajv';
|
|
32
32
|
import addFormats from 'ajv-formats';
|
|
33
|
-
import addErrors from 'ajv-errors';
|
|
34
33
|
import dayjs from 'dayjs';
|
|
35
34
|
import utc from 'dayjs/plugin/utc';
|
|
36
35
|
import timezone from 'dayjs/plugin/timezone';
|
|
@@ -3644,16 +3643,6 @@ const TableDataDisplay = ({ colorPalette, emptyComponent, }) => {
|
|
|
3644
3643
|
})] }));
|
|
3645
3644
|
};
|
|
3646
3645
|
|
|
3647
|
-
const AccordionItemTrigger = React.forwardRef(function AccordionItemTrigger(props, ref) {
|
|
3648
|
-
const { children, indicatorPlacement = "end", ...rest } = props;
|
|
3649
|
-
return (jsxs(Accordion.ItemTrigger, { ...rest, ref: ref, children: [indicatorPlacement === "start" && (jsx(Accordion.ItemIndicator, { rotate: { base: "-90deg", _open: "0deg" }, children: jsx(LuChevronDown, {}) })), jsx(HStack, { gap: "4", flex: "1", textAlign: "start", width: "full", children: children }), indicatorPlacement === "end" && (jsx(Accordion.ItemIndicator, { children: jsx(LuChevronDown, {}) }))] }));
|
|
3650
|
-
});
|
|
3651
|
-
const AccordionItemContent = React.forwardRef(function AccordionItemContent(props, ref) {
|
|
3652
|
-
return (jsx(Accordion.ItemContent, { children: jsx(Accordion.ItemBody, { ...props, ref: ref }) }));
|
|
3653
|
-
});
|
|
3654
|
-
const AccordionRoot = Accordion.Root;
|
|
3655
|
-
const AccordionItem = Accordion.Item;
|
|
3656
|
-
|
|
3657
3646
|
//@ts-expect-error TODO: find appropriate type
|
|
3658
3647
|
const SchemaFormContext = createContext({
|
|
3659
3648
|
schema: {},
|
|
@@ -3673,19 +3662,23 @@ const SchemaFormContext = createContext({
|
|
|
3673
3662
|
},
|
|
3674
3663
|
requireConfirmation: false,
|
|
3675
3664
|
onFormSubmit: async () => { },
|
|
3665
|
+
ajvResolver: async () => ({ values: {}, errors: {} }),
|
|
3676
3666
|
});
|
|
3677
3667
|
|
|
3678
3668
|
const useSchemaContext = () => {
|
|
3679
3669
|
return useContext(SchemaFormContext);
|
|
3680
3670
|
};
|
|
3681
3671
|
|
|
3672
|
+
const clearEmptyString = (object) => {
|
|
3673
|
+
return Object.fromEntries(Object.entries(object).filter(([, value]) => value !== ""));
|
|
3674
|
+
};
|
|
3675
|
+
|
|
3682
3676
|
const validateData = (data, schema) => {
|
|
3683
3677
|
const ajv = new Ajv({
|
|
3684
3678
|
strict: false,
|
|
3685
3679
|
allErrors: true,
|
|
3686
3680
|
});
|
|
3687
3681
|
addFormats(ajv);
|
|
3688
|
-
addErrors(ajv);
|
|
3689
3682
|
const validate = ajv.compile(schema);
|
|
3690
3683
|
const validationResult = validate(data);
|
|
3691
3684
|
const errors = validate.errors;
|
|
@@ -3697,8 +3690,183 @@ const validateData = (data, schema) => {
|
|
|
3697
3690
|
};
|
|
3698
3691
|
};
|
|
3699
3692
|
|
|
3700
|
-
|
|
3701
|
-
|
|
3693
|
+
/**
|
|
3694
|
+
* Gets the schema node for a field by following the path from root schema
|
|
3695
|
+
*/
|
|
3696
|
+
const getSchemaNodeForField = (schema, fieldPath) => {
|
|
3697
|
+
if (!fieldPath || fieldPath === '') {
|
|
3698
|
+
return schema;
|
|
3699
|
+
}
|
|
3700
|
+
const pathParts = fieldPath.split('.');
|
|
3701
|
+
let currentSchema = schema;
|
|
3702
|
+
for (const part of pathParts) {
|
|
3703
|
+
if (currentSchema &&
|
|
3704
|
+
currentSchema.properties &&
|
|
3705
|
+
currentSchema.properties[part] &&
|
|
3706
|
+
typeof currentSchema.properties[part] === 'object' &&
|
|
3707
|
+
currentSchema.properties[part] !== null) {
|
|
3708
|
+
currentSchema = currentSchema.properties[part];
|
|
3709
|
+
}
|
|
3710
|
+
else {
|
|
3711
|
+
return undefined;
|
|
3712
|
+
}
|
|
3713
|
+
}
|
|
3714
|
+
return currentSchema;
|
|
3715
|
+
};
|
|
3716
|
+
/**
|
|
3717
|
+
* Converts AJV error objects to react-hook-form field errors format
|
|
3718
|
+
*/
|
|
3719
|
+
const convertAjvErrorsToFieldErrors = (errors, schema) => {
|
|
3720
|
+
if (!errors || errors.length === 0) {
|
|
3721
|
+
return {};
|
|
3722
|
+
}
|
|
3723
|
+
const fieldErrors = {};
|
|
3724
|
+
errors.forEach((error) => {
|
|
3725
|
+
let fieldName = '';
|
|
3726
|
+
// Special handling for required keyword: map to the specific missing property
|
|
3727
|
+
if (error.keyword === 'required') {
|
|
3728
|
+
const basePath = (error.instancePath || '')
|
|
3729
|
+
.replace(/^\//, '')
|
|
3730
|
+
.replace(/\//g, '.');
|
|
3731
|
+
const missingProperty = (error.params &&
|
|
3732
|
+
error.params.missingProperty);
|
|
3733
|
+
if (missingProperty) {
|
|
3734
|
+
fieldName = basePath
|
|
3735
|
+
? `${basePath}.${missingProperty}`
|
|
3736
|
+
: missingProperty;
|
|
3737
|
+
}
|
|
3738
|
+
else {
|
|
3739
|
+
// Fallback to schemaPath conversion if missingProperty is unavailable
|
|
3740
|
+
fieldName = (error.schemaPath || '')
|
|
3741
|
+
.replace(/^#\//, '#.')
|
|
3742
|
+
.replace(/\//g, '.');
|
|
3743
|
+
}
|
|
3744
|
+
}
|
|
3745
|
+
else {
|
|
3746
|
+
const fieldPath = error.instancePath || error.schemaPath;
|
|
3747
|
+
if (fieldPath) {
|
|
3748
|
+
fieldName = fieldPath.replace(/^\//, '').replace(/\//g, '.');
|
|
3749
|
+
}
|
|
3750
|
+
}
|
|
3751
|
+
if (fieldName) {
|
|
3752
|
+
// Get the schema node for this field to check for custom error messages
|
|
3753
|
+
const fieldSchema = getSchemaNodeForField(schema, fieldName);
|
|
3754
|
+
const customMessage = fieldSchema?.errorMessages?.[error.keyword];
|
|
3755
|
+
// Provide helpful fallback message if no custom message is provided
|
|
3756
|
+
const fallbackMessage = customMessage ||
|
|
3757
|
+
`Missing error message for ${error.keyword}. Add errorMessages.${error.keyword} to schema for field '${fieldName}'`;
|
|
3758
|
+
if (error.keyword === 'required') {
|
|
3759
|
+
// Required errors override any existing non-required errors for this field
|
|
3760
|
+
fieldErrors[fieldName] = {
|
|
3761
|
+
type: 'required',
|
|
3762
|
+
keyword: error.keyword,
|
|
3763
|
+
params: error.params,
|
|
3764
|
+
message: fallbackMessage,
|
|
3765
|
+
};
|
|
3766
|
+
}
|
|
3767
|
+
else {
|
|
3768
|
+
const existing = fieldErrors[fieldName];
|
|
3769
|
+
if (existing) {
|
|
3770
|
+
// Do not override required errors
|
|
3771
|
+
if (existing.type === 'required') {
|
|
3772
|
+
return;
|
|
3773
|
+
}
|
|
3774
|
+
// Combine messages if multiple errors for same field
|
|
3775
|
+
existing.message = existing.message
|
|
3776
|
+
? `${existing.message}; ${fallbackMessage}`
|
|
3777
|
+
: fallbackMessage;
|
|
3778
|
+
}
|
|
3779
|
+
else {
|
|
3780
|
+
fieldErrors[fieldName] = {
|
|
3781
|
+
type: error.keyword,
|
|
3782
|
+
keyword: error.keyword,
|
|
3783
|
+
params: error.params,
|
|
3784
|
+
message: fallbackMessage,
|
|
3785
|
+
};
|
|
3786
|
+
}
|
|
3787
|
+
}
|
|
3788
|
+
}
|
|
3789
|
+
});
|
|
3790
|
+
return fieldErrors;
|
|
3791
|
+
};
|
|
3792
|
+
/**
|
|
3793
|
+
* AJV resolver for react-hook-form
|
|
3794
|
+
* Integrates AJV validation with react-hook-form's validation system
|
|
3795
|
+
*/
|
|
3796
|
+
/**
|
|
3797
|
+
* Strips null, undefined, and empty string values from an object
|
|
3798
|
+
*/
|
|
3799
|
+
const stripEmptyValues = (obj) => {
|
|
3800
|
+
if (obj === null || obj === undefined) {
|
|
3801
|
+
return undefined;
|
|
3802
|
+
}
|
|
3803
|
+
if (typeof obj === 'string' && obj.trim() === '') {
|
|
3804
|
+
return undefined;
|
|
3805
|
+
}
|
|
3806
|
+
if (Array.isArray(obj)) {
|
|
3807
|
+
const filtered = obj
|
|
3808
|
+
.map(stripEmptyValues)
|
|
3809
|
+
.filter((item) => item !== undefined);
|
|
3810
|
+
return filtered.length > 0 ? filtered : undefined;
|
|
3811
|
+
}
|
|
3812
|
+
if (typeof obj === 'object' && obj !== null) {
|
|
3813
|
+
const result = {};
|
|
3814
|
+
let hasValues = false;
|
|
3815
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
3816
|
+
const cleanedValue = stripEmptyValues(value);
|
|
3817
|
+
if (cleanedValue !== undefined) {
|
|
3818
|
+
result[key] = cleanedValue;
|
|
3819
|
+
hasValues = true;
|
|
3820
|
+
}
|
|
3821
|
+
}
|
|
3822
|
+
return hasValues ? result : undefined;
|
|
3823
|
+
}
|
|
3824
|
+
return obj;
|
|
3825
|
+
};
|
|
3826
|
+
const ajvResolver = (schema) => {
|
|
3827
|
+
return async (values) => {
|
|
3828
|
+
try {
|
|
3829
|
+
// Strip empty values before validation
|
|
3830
|
+
const cleanedValues = stripEmptyValues(values);
|
|
3831
|
+
// Use empty object for validation if all values were stripped
|
|
3832
|
+
const valuesToValidate = cleanedValues === undefined ? {} : cleanedValues;
|
|
3833
|
+
const { isValid, errors } = validateData(valuesToValidate, schema);
|
|
3834
|
+
console.debug('AJV Validation Result:', {
|
|
3835
|
+
isValid,
|
|
3836
|
+
errors,
|
|
3837
|
+
cleanedValues,
|
|
3838
|
+
valuesToValidate,
|
|
3839
|
+
});
|
|
3840
|
+
if (isValid) {
|
|
3841
|
+
return {
|
|
3842
|
+
values: (cleanedValues || {}),
|
|
3843
|
+
errors: {},
|
|
3844
|
+
};
|
|
3845
|
+
}
|
|
3846
|
+
const fieldErrors = convertAjvErrorsToFieldErrors(errors, schema);
|
|
3847
|
+
console.debug('AJV Validation Failed:', {
|
|
3848
|
+
errors,
|
|
3849
|
+
fieldErrors,
|
|
3850
|
+
cleanedValues,
|
|
3851
|
+
valuesToValidate,
|
|
3852
|
+
});
|
|
3853
|
+
return {
|
|
3854
|
+
values: {},
|
|
3855
|
+
errors: fieldErrors,
|
|
3856
|
+
};
|
|
3857
|
+
}
|
|
3858
|
+
catch (error) {
|
|
3859
|
+
return {
|
|
3860
|
+
values: {},
|
|
3861
|
+
errors: {
|
|
3862
|
+
root: {
|
|
3863
|
+
type: 'validation',
|
|
3864
|
+
message: error instanceof Error ? error.message : 'Validation failed',
|
|
3865
|
+
},
|
|
3866
|
+
},
|
|
3867
|
+
};
|
|
3868
|
+
}
|
|
3869
|
+
};
|
|
3702
3870
|
};
|
|
3703
3871
|
|
|
3704
3872
|
const idPickerSanityCheck = (column, foreign_key) => {
|
|
@@ -3720,7 +3888,7 @@ const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, childre
|
|
|
3720
3888
|
showSubmitButton: true,
|
|
3721
3889
|
showResetButton: true,
|
|
3722
3890
|
showTitle: true,
|
|
3723
|
-
}, requireConfirmation = false, dateTimePickerLabels, idPickerLabels, enumPickerLabels, }) => {
|
|
3891
|
+
}, requireConfirmation = false, dateTimePickerLabels, idPickerLabels, enumPickerLabels, filePickerLabels, }) => {
|
|
3724
3892
|
const [isSuccess, setIsSuccess] = useState(false);
|
|
3725
3893
|
const [isError, setIsError] = useState(false);
|
|
3726
3894
|
const [isSubmiting, setIsSubmiting] = useState(false);
|
|
@@ -3740,33 +3908,16 @@ const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, childre
|
|
|
3740
3908
|
const onSubmitSuccess = () => {
|
|
3741
3909
|
setIsSuccess(true);
|
|
3742
3910
|
};
|
|
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
3911
|
const defaultOnSubmit = async (promise) => {
|
|
3764
3912
|
try {
|
|
3913
|
+
console.log('onBeforeSubmit');
|
|
3765
3914
|
onBeforeSubmit();
|
|
3766
3915
|
await promise;
|
|
3916
|
+
console.log('onSubmitSuccess');
|
|
3767
3917
|
onSubmitSuccess();
|
|
3768
3918
|
}
|
|
3769
3919
|
catch (error) {
|
|
3920
|
+
console.log('onSubmitError', error);
|
|
3770
3921
|
onSubmitError(error);
|
|
3771
3922
|
}
|
|
3772
3923
|
finally {
|
|
@@ -3776,7 +3927,7 @@ const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, childre
|
|
|
3776
3927
|
const defaultSubmitPromise = (data) => {
|
|
3777
3928
|
const options = {
|
|
3778
3929
|
method: 'POST',
|
|
3779
|
-
url: `${
|
|
3930
|
+
url: `${serverUrl}`,
|
|
3780
3931
|
data: clearEmptyString(data),
|
|
3781
3932
|
...requestOptions,
|
|
3782
3933
|
};
|
|
@@ -3784,23 +3935,13 @@ const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, childre
|
|
|
3784
3935
|
};
|
|
3785
3936
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3786
3937
|
const onFormSubmit = async (data) => {
|
|
3787
|
-
//
|
|
3788
|
-
|
|
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
|
-
}
|
|
3938
|
+
// AJV validation is now handled by react-hook-form resolver
|
|
3939
|
+
// This function will only be called if validation passes
|
|
3799
3940
|
if (onSubmit === undefined) {
|
|
3800
|
-
await defaultOnSubmit(defaultSubmitPromise(data));
|
|
3941
|
+
await defaultOnSubmit(Promise.resolve(defaultSubmitPromise(data)));
|
|
3801
3942
|
return;
|
|
3802
3943
|
}
|
|
3803
|
-
await defaultOnSubmit(onSubmit(data));
|
|
3944
|
+
await defaultOnSubmit(Promise.resolve(onSubmit(data)));
|
|
3804
3945
|
};
|
|
3805
3946
|
return (jsx(SchemaFormContext.Provider, { value: {
|
|
3806
3947
|
schema,
|
|
@@ -3836,6 +3977,8 @@ const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, childre
|
|
|
3836
3977
|
dateTimePickerLabels,
|
|
3837
3978
|
idPickerLabels,
|
|
3838
3979
|
enumPickerLabels,
|
|
3980
|
+
filePickerLabels,
|
|
3981
|
+
ajvResolver: ajvResolver(schema),
|
|
3839
3982
|
}, children: jsx(FormProvider, { ...form, children: children }) }));
|
|
3840
3983
|
};
|
|
3841
3984
|
|
|
@@ -3881,20 +4024,22 @@ const ArrayRenderer = ({ schema, column, prefix, }) => {
|
|
|
3881
4024
|
|
|
3882
4025
|
const Field = React.forwardRef(function Field(props, ref) {
|
|
3883
4026
|
const { label, children, helperText, errorText, optionalText, ...rest } = props;
|
|
3884
|
-
return (jsxs(Field$1.Root, { ref: ref, ...rest, children: [label && (jsxs(Field$1.Label, { children: [label, jsx(Field$1.RequiredIndicator, { fallback: optionalText })] })), children, helperText && (jsx(Field$1.HelperText, { children: helperText })), errorText && (
|
|
4027
|
+
return (jsxs(Field$1.Root, { ref: ref, ...rest, children: [label && (jsxs(Field$1.Label, { children: [label, jsx(Field$1.RequiredIndicator, { color: rest.invalid && rest.required ? 'red.500' : undefined, fallback: optionalText })] })), children, helperText && (jsx(Field$1.HelperText, { children: helperText })), !!errorText && (jsxs(Field$1.ErrorText, { children: [rest.required && rest.invalid && (jsx("span", { style: { color: 'var(--chakra-colors-red-500)' }, children: "* " })), errorText] }))] }));
|
|
3885
4028
|
});
|
|
3886
4029
|
|
|
3887
4030
|
const BooleanPicker = ({ schema, column, prefix }) => {
|
|
3888
4031
|
const { watch, formState: { errors }, setValue, } = useFormContext();
|
|
3889
4032
|
const { translate } = useSchemaContext();
|
|
3890
|
-
const { required, gridColumn =
|
|
4033
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1' } = schema;
|
|
3891
4034
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
3892
4035
|
const colLabel = `${prefix}${column}`;
|
|
3893
4036
|
const value = watch(colLabel);
|
|
3894
|
-
return (
|
|
3895
|
-
gridRow,
|
|
3896
|
-
|
|
3897
|
-
|
|
4037
|
+
return (jsx(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, alignItems: 'stretch', gridColumn,
|
|
4038
|
+
gridRow, errorText: errors[`${colLabel}`]
|
|
4039
|
+
? translate.t(removeIndex(`${colLabel}.field_required`))
|
|
4040
|
+
: undefined, invalid: !!errors[colLabel], children: jsx(CheckboxCard, { checked: value, variant: 'surface', onChange: () => {
|
|
4041
|
+
setValue(colLabel, !value);
|
|
4042
|
+
} }) }));
|
|
3898
4043
|
};
|
|
3899
4044
|
|
|
3900
4045
|
const CustomInput = ({ column, schema, prefix }) => {
|
|
@@ -4127,83 +4272,169 @@ const DatePicker = ({ column, schema, prefix }) => {
|
|
|
4127
4272
|
console.error(e);
|
|
4128
4273
|
}
|
|
4129
4274
|
}, [selectedDate, dateFormat, colLabel, setValue]);
|
|
4130
|
-
return (
|
|
4131
|
-
gridRow, children:
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4275
|
+
return (jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
4276
|
+
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], 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: () => {
|
|
4277
|
+
setOpen(true);
|
|
4278
|
+
}, 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 }) => {
|
|
4279
|
+
setValue(colLabel, dayjs(date).format(dateFormat));
|
|
4280
|
+
setOpen(false);
|
|
4281
|
+
}, labels: {
|
|
4282
|
+
monthNamesShort: dateTimePickerLabels?.monthNamesShort ?? [
|
|
4283
|
+
formI18n.translate.t(`common.month_1`, {
|
|
4284
|
+
defaultValue: 'January',
|
|
4285
|
+
}),
|
|
4286
|
+
formI18n.translate.t(`common.month_2`, {
|
|
4287
|
+
defaultValue: 'February',
|
|
4288
|
+
}),
|
|
4289
|
+
formI18n.translate.t(`common.month_3`, {
|
|
4290
|
+
defaultValue: 'March',
|
|
4291
|
+
}),
|
|
4292
|
+
formI18n.translate.t(`common.month_4`, {
|
|
4293
|
+
defaultValue: 'April',
|
|
4294
|
+
}),
|
|
4295
|
+
formI18n.translate.t(`common.month_5`, {
|
|
4296
|
+
defaultValue: 'May',
|
|
4297
|
+
}),
|
|
4298
|
+
formI18n.translate.t(`common.month_6`, {
|
|
4299
|
+
defaultValue: 'June',
|
|
4300
|
+
}),
|
|
4301
|
+
formI18n.translate.t(`common.month_7`, {
|
|
4302
|
+
defaultValue: 'July',
|
|
4303
|
+
}),
|
|
4304
|
+
formI18n.translate.t(`common.month_8`, {
|
|
4305
|
+
defaultValue: 'August',
|
|
4306
|
+
}),
|
|
4307
|
+
formI18n.translate.t(`common.month_9`, {
|
|
4308
|
+
defaultValue: 'September',
|
|
4309
|
+
}),
|
|
4310
|
+
formI18n.translate.t(`common.month_10`, {
|
|
4311
|
+
defaultValue: 'October',
|
|
4312
|
+
}),
|
|
4313
|
+
formI18n.translate.t(`common.month_11`, {
|
|
4314
|
+
defaultValue: 'November',
|
|
4315
|
+
}),
|
|
4316
|
+
formI18n.translate.t(`common.month_12`, {
|
|
4317
|
+
defaultValue: 'December',
|
|
4318
|
+
}),
|
|
4319
|
+
],
|
|
4320
|
+
weekdayNamesShort: dateTimePickerLabels?.weekdayNamesShort ?? [
|
|
4321
|
+
formI18n.translate.t(`common.weekday_1`, {
|
|
4322
|
+
defaultValue: 'Sun',
|
|
4323
|
+
}),
|
|
4324
|
+
formI18n.translate.t(`common.weekday_2`, {
|
|
4325
|
+
defaultValue: 'Mon',
|
|
4326
|
+
}),
|
|
4327
|
+
formI18n.translate.t(`common.weekday_3`, {
|
|
4328
|
+
defaultValue: 'Tue',
|
|
4329
|
+
}),
|
|
4330
|
+
formI18n.translate.t(`common.weekday_4`, {
|
|
4331
|
+
defaultValue: 'Wed',
|
|
4332
|
+
}),
|
|
4333
|
+
formI18n.translate.t(`common.weekday_5`, {
|
|
4334
|
+
defaultValue: 'Thu',
|
|
4335
|
+
}),
|
|
4336
|
+
formI18n.translate.t(`common.weekday_6`, {
|
|
4337
|
+
defaultValue: 'Fri',
|
|
4338
|
+
}),
|
|
4339
|
+
formI18n.translate.t(`common.weekday_7`, {
|
|
4340
|
+
defaultValue: 'Sat',
|
|
4341
|
+
}),
|
|
4342
|
+
],
|
|
4343
|
+
backButtonLabel: dateTimePickerLabels?.backButtonLabel ??
|
|
4344
|
+
formI18n.translate.t(`common.back_button`, {
|
|
4345
|
+
defaultValue: 'Back',
|
|
4346
|
+
}),
|
|
4347
|
+
forwardButtonLabel: dateTimePickerLabels?.forwardButtonLabel ??
|
|
4348
|
+
formI18n.translate.t(`common.forward_button`, {
|
|
4349
|
+
defaultValue: 'Forward',
|
|
4350
|
+
}),
|
|
4351
|
+
} })] }) })] }) }));
|
|
4352
|
+
};
|
|
4353
|
+
|
|
4354
|
+
dayjs.extend(utc);
|
|
4355
|
+
dayjs.extend(timezone);
|
|
4356
|
+
const DateRangePicker = ({ column, schema, prefix, }) => {
|
|
4357
|
+
const { watch, formState: { errors }, setValue, } = useFormContext();
|
|
4358
|
+
const { timezone, dateTimePickerLabels } = useSchemaContext();
|
|
4359
|
+
const formI18n = useFormI18n(column, prefix);
|
|
4360
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1', displayDateFormat = 'YYYY-MM-DD', dateFormat = 'YYYY-MM-DD', } = schema;
|
|
4361
|
+
const isRequired = required?.some((columnId) => columnId === column);
|
|
4362
|
+
const colLabel = formI18n.colLabel;
|
|
4363
|
+
const [open, setOpen] = useState(false);
|
|
4364
|
+
const selectedDateRange = watch(colLabel);
|
|
4365
|
+
// Convert string[] to Date[] for the picker
|
|
4366
|
+
const selectedDates = (selectedDateRange ?? [])
|
|
4367
|
+
.map((dateStr) => {
|
|
4368
|
+
if (!dateStr)
|
|
4369
|
+
return null;
|
|
4370
|
+
const parsed = dayjs(dateStr).tz(timezone);
|
|
4371
|
+
return parsed.isValid() ? parsed.toDate() : null;
|
|
4372
|
+
})
|
|
4373
|
+
.filter((date) => date !== null);
|
|
4374
|
+
// Format display string
|
|
4375
|
+
const getDisplayText = () => {
|
|
4376
|
+
if (!selectedDateRange || selectedDateRange.length === 0) {
|
|
4377
|
+
return '';
|
|
4378
|
+
}
|
|
4379
|
+
if (selectedDateRange.length === 1) {
|
|
4380
|
+
const date = dayjs(selectedDateRange[0]).tz(timezone);
|
|
4381
|
+
return date.isValid() ? date.format(displayDateFormat) : '';
|
|
4382
|
+
}
|
|
4383
|
+
if (selectedDateRange.length === 2) {
|
|
4384
|
+
const startDate = dayjs(selectedDateRange[0]).tz(timezone);
|
|
4385
|
+
const endDate = dayjs(selectedDateRange[1]).tz(timezone);
|
|
4386
|
+
if (startDate.isValid() && endDate.isValid()) {
|
|
4387
|
+
return `${startDate.format(displayDateFormat)} - ${endDate.format(displayDateFormat)}`;
|
|
4388
|
+
}
|
|
4389
|
+
}
|
|
4390
|
+
return '';
|
|
4391
|
+
};
|
|
4392
|
+
useEffect(() => {
|
|
4393
|
+
try {
|
|
4394
|
+
if (selectedDateRange && selectedDateRange.length > 0) {
|
|
4395
|
+
// Format dates according to dateFormat from schema
|
|
4396
|
+
const formatted = selectedDateRange
|
|
4397
|
+
.map((dateStr) => {
|
|
4398
|
+
if (!dateStr)
|
|
4399
|
+
return null;
|
|
4400
|
+
const parsed = dayjs(dateStr).tz(timezone);
|
|
4401
|
+
return parsed.isValid() ? parsed.format(dateFormat) : null;
|
|
4402
|
+
})
|
|
4403
|
+
.filter((date) => date !== null);
|
|
4404
|
+
// Update the form value only if different to avoid loops
|
|
4405
|
+
// Compare arrays element by element
|
|
4406
|
+
const needsUpdate = formatted.length !== selectedDateRange.length ||
|
|
4407
|
+
formatted.some((val, idx) => val !== selectedDateRange[idx]);
|
|
4408
|
+
if (needsUpdate && formatted.length > 0) {
|
|
4409
|
+
setValue(colLabel, formatted, {
|
|
4410
|
+
shouldValidate: true,
|
|
4411
|
+
shouldDirty: true,
|
|
4412
|
+
});
|
|
4413
|
+
}
|
|
4414
|
+
}
|
|
4415
|
+
}
|
|
4416
|
+
catch (e) {
|
|
4417
|
+
console.error(e);
|
|
4418
|
+
}
|
|
4419
|
+
}, [selectedDateRange, dateFormat, colLabel, setValue, timezone]);
|
|
4420
|
+
return (jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
4421
|
+
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], 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: () => {
|
|
4422
|
+
setOpen(true);
|
|
4423
|
+
}, justifyContent: 'start', children: [jsx(MdDateRange, {}), getDisplayText()] }) }), jsx(PopoverContent, { minW: '600px', children: jsxs(PopoverBody, { children: [jsx(PopoverTitle, {}), jsx(RangeDatePicker, { selected: selectedDates, onDateSelected: ({ selected, selectable, date }) => {
|
|
4424
|
+
const newDates = getRangeDates({
|
|
4425
|
+
selectable,
|
|
4426
|
+
date,
|
|
4427
|
+
selectedDates,
|
|
4428
|
+
}) ?? [];
|
|
4429
|
+
// Convert Date[] to string[]
|
|
4430
|
+
const formattedDates = newDates
|
|
4431
|
+
.map((dateObj) => dayjs(dateObj).tz(timezone).format(dateFormat))
|
|
4432
|
+
.filter((dateStr) => dateStr);
|
|
4433
|
+
setValue(colLabel, formattedDates, {
|
|
4434
|
+
shouldValidate: true,
|
|
4435
|
+
shouldDirty: true,
|
|
4436
|
+
});
|
|
4437
|
+
}, monthsToDisplay: 2 })] }) })] }) }));
|
|
4207
4438
|
};
|
|
4208
4439
|
|
|
4209
4440
|
function filterArray(array, searchTerm) {
|
|
@@ -4238,7 +4469,9 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
|
|
|
4238
4469
|
};
|
|
4239
4470
|
if (variant === 'radio') {
|
|
4240
4471
|
return (jsx(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, alignItems: 'stretch', gridColumn,
|
|
4241
|
-
gridRow,
|
|
4472
|
+
gridRow, errorText: errors[`${colLabel}`]
|
|
4473
|
+
? translate.t(removeIndex(`${colLabel}.field_required`))
|
|
4474
|
+
: undefined, invalid: !!errors[colLabel], children: jsx(RadioGroup$1.Root, { defaultValue: "1", children: jsx(HStack, { gap: "6", children: filterArray(dataList, searchText ?? '').map((item) => {
|
|
4242
4475
|
return (jsxs(RadioGroup$1.Item, { onClick: () => {
|
|
4243
4476
|
if (!isMultiple) {
|
|
4244
4477
|
setOpenSearchResult(false);
|
|
@@ -4253,7 +4486,9 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
|
|
|
4253
4486
|
}) }) }) }));
|
|
4254
4487
|
}
|
|
4255
4488
|
return (jsxs(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, alignItems: 'stretch', gridColumn,
|
|
4256
|
-
gridRow,
|
|
4489
|
+
gridRow, errorText: errors[`${colLabel}`]
|
|
4490
|
+
? translate.t(removeIndex(`${colLabel}.field_required`))
|
|
4491
|
+
: undefined, invalid: !!errors[colLabel], children: [isMultiple && (jsxs(Flex, { flexFlow: 'wrap', gap: 1, children: [watchEnums.map((enumValue) => {
|
|
4257
4492
|
const item = enumValue;
|
|
4258
4493
|
if (!!item === false) {
|
|
4259
4494
|
return jsx(Fragment, {});
|
|
@@ -4308,7 +4543,7 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
|
|
|
4308
4543
|
? renderDisplay(item)
|
|
4309
4544
|
: translate.t(removeIndex(`${colLabel}.${item}`)) }, `${colLabel}-${item}`));
|
|
4310
4545
|
}) }), isDirty && (jsx(Fragment, { children: dataList.length <= 0 && (jsx(Fragment, { children: enumPickerLabels?.emptySearchResult ??
|
|
4311
|
-
translate.t(removeIndex(`${colLabel}.empty_search_result`)) })) }))] })] }) })] })
|
|
4546
|
+
translate.t(removeIndex(`${colLabel}.empty_search_result`)) })) }))] })] }) })] })] }));
|
|
4312
4547
|
};
|
|
4313
4548
|
|
|
4314
4549
|
function isEnteringWindow(_ref) {
|
|
@@ -4663,23 +4898,162 @@ const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }
|
|
|
4663
4898
|
return (jsxs(Grid, { ...getColor(isDraggedOver), ref: ref, cursor: "pointer", onClick: handleClick, borderStyle: "dashed", borderColor: "colorPalette.400", alignContent: "center", justifyContent: "center", borderWidth: 1, borderRadius: 4, ...gridProps, children: [children, !!children === false && (jsxs(Fragment, { children: [jsx(Flex, { children: placeholder }), jsx(Input, { type: "file", multiple: true, style: { display: "none" }, ref: fileInput, onChange: handleChange })] }))] }));
|
|
4664
4899
|
};
|
|
4665
4900
|
|
|
4901
|
+
/**
|
|
4902
|
+
* Format bytes to human-readable string
|
|
4903
|
+
* @param bytes - The number of bytes to format
|
|
4904
|
+
* @returns Formatted string (e.g., "1.5 KB", "2.3 MB")
|
|
4905
|
+
*/
|
|
4906
|
+
function formatBytes(bytes) {
|
|
4907
|
+
if (bytes === 0)
|
|
4908
|
+
return '0 Bytes';
|
|
4909
|
+
const k = 1024;
|
|
4910
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
|
4911
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
4912
|
+
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
|
4913
|
+
}
|
|
4914
|
+
|
|
4915
|
+
function FilePickerDialog({ open, onClose, onSelect, title, filterImageOnly = false, onFetchFiles, labels, translate, colLabel, }) {
|
|
4916
|
+
const [searchTerm, setSearchTerm] = useState('');
|
|
4917
|
+
const [selectedFileId, setSelectedFileId] = useState('');
|
|
4918
|
+
const { data: filesData, isLoading, isError, } = useQuery({
|
|
4919
|
+
queryKey: ['file-picker-library', searchTerm],
|
|
4920
|
+
queryFn: async () => {
|
|
4921
|
+
if (!onFetchFiles)
|
|
4922
|
+
return { data: [] };
|
|
4923
|
+
const files = await onFetchFiles(searchTerm.trim() || '');
|
|
4924
|
+
return { data: files };
|
|
4925
|
+
},
|
|
4926
|
+
enabled: open && !!onFetchFiles,
|
|
4927
|
+
});
|
|
4928
|
+
const files = (filesData?.data || []);
|
|
4929
|
+
const filteredFiles = filterImageOnly
|
|
4930
|
+
? files.filter((file) => /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file.name))
|
|
4931
|
+
: files;
|
|
4932
|
+
const handleSelect = () => {
|
|
4933
|
+
if (selectedFileId) {
|
|
4934
|
+
onSelect(selectedFileId);
|
|
4935
|
+
onClose();
|
|
4936
|
+
setSelectedFileId('');
|
|
4937
|
+
setSearchTerm('');
|
|
4938
|
+
}
|
|
4939
|
+
};
|
|
4940
|
+
const handleClose = () => {
|
|
4941
|
+
onClose();
|
|
4942
|
+
setSelectedFileId('');
|
|
4943
|
+
setSearchTerm('');
|
|
4944
|
+
};
|
|
4945
|
+
if (!onFetchFiles)
|
|
4946
|
+
return null;
|
|
4947
|
+
return (jsx(DialogRoot, { open: open, onOpenChange: (e) => !e.open && handleClose(), children: jsxs(DialogContent, { maxWidth: "800px", maxHeight: "90vh", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { fontSize: "lg", fontWeight: "bold", children: title }), jsx(DialogCloseTrigger, {})] }), jsx(DialogBody, { children: jsxs(VStack, { align: "stretch", gap: 4, children: [jsxs(Box, { position: "relative", children: [jsx(Input, { placeholder: labels?.searchPlaceholder ??
|
|
4948
|
+
translate(removeIndex(`${colLabel}.search_placeholder`)) ??
|
|
4949
|
+
'Search files...', value: searchTerm, onChange: (e) => setSearchTerm(e.target.value), bg: "bg.panel", border: "1px solid", borderColor: "border.default", _focus: {
|
|
4950
|
+
borderColor: 'blue.500',
|
|
4951
|
+
boxShadow: '0 0 0 1px var(--chakra-colors-blue-500)',
|
|
4952
|
+
}, pl: 10 }), jsx(Icon, { as: LuSearch, position: "absolute", left: 3, top: "50%", transform: "translateY(-50%)", color: "fg.muted", boxSize: 4 })] }), isLoading && (jsxs(Box, { textAlign: "center", py: 8, children: [jsx(Spinner, { size: "lg", colorPalette: "blue" }), jsx(Text, { mt: 4, color: "fg.muted", children: labels?.loading ??
|
|
4953
|
+
translate(removeIndex(`${colLabel}.loading`)) ??
|
|
4954
|
+
'Loading files...' })] })), isError && (jsx(Box, { bg: "red.50", _dark: { bg: 'red.900/20' }, border: "1px solid", borderColor: "red.200", borderRadius: "md", p: 4, children: jsx(Text, { color: "red.600", _dark: { color: 'red.300' }, children: labels?.loadingFailed ??
|
|
4955
|
+
translate(removeIndex(`${colLabel}.error.loading_failed`)) ??
|
|
4956
|
+
'Failed to load files' }) })), !isLoading && !isError && (jsx(Box, { maxHeight: "400px", overflowY: "auto", children: filteredFiles.length === 0 ? (jsx(Box, { textAlign: "center", py: 8, children: jsx(Text, { color: "fg.muted", children: labels?.noFilesFound ??
|
|
4957
|
+
translate(removeIndex(`${colLabel}.no_files_found`)) ??
|
|
4958
|
+
'No files found' }) })) : (jsx(VStack, { align: "stretch", gap: 2, children: filteredFiles.map((file) => {
|
|
4959
|
+
const isImage = /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file.name);
|
|
4960
|
+
const isSelected = selectedFileId === file.id;
|
|
4961
|
+
return (jsx(Box, { p: 3, border: "2px solid", borderColor: isSelected ? 'blue.500' : 'border.default', borderRadius: "md", bg: isSelected ? 'blue.50' : 'bg.panel', _dark: {
|
|
4962
|
+
bg: isSelected ? 'blue.900/20' : 'bg.panel',
|
|
4963
|
+
}, cursor: "pointer", onClick: () => setSelectedFileId(file.id), _hover: {
|
|
4964
|
+
borderColor: isSelected ? 'blue.600' : 'blue.300',
|
|
4965
|
+
bg: isSelected ? 'blue.100' : 'bg.muted',
|
|
4966
|
+
}, transition: "all 0.2s", children: jsxs(HStack, { gap: 3, children: [jsx(Box, { width: "60px", height: "60px", display: "flex", alignItems: "center", justifyContent: "center", bg: "bg.muted", borderRadius: "md", flexShrink: 0, children: isImage && file.url ? (jsx(Image, { src: file.url, alt: file.name, boxSize: "60px", objectFit: "cover", borderRadius: "md" })) : (jsx(Icon, { as: LuFile, boxSize: 6, color: "fg.muted" })) }), jsxs(VStack, { align: "start", flex: 1, gap: 1, children: [jsx(Text, { fontSize: "sm", fontWeight: "medium", color: "fg.default", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: file.name }), jsxs(HStack, { gap: 2, children: [file.size && (jsx(Fragment, { children: jsx(Text, { fontSize: "xs", color: "fg.muted", children: typeof file.size === 'number'
|
|
4967
|
+
? formatBytes(file.size)
|
|
4968
|
+
: file.size }) })), file.comment && (jsxs(Fragment, { children: [file.size && (jsx(Text, { fontSize: "xs", color: "fg.muted", children: "\u2022" })), jsx(Text, { fontSize: "xs", color: "fg.muted", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: file.comment })] }))] })] }), isSelected && (jsx(Box, { width: "24px", height: "24px", borderRadius: "full", bg: "blue.500", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0, children: jsx(Text, { color: "white", fontSize: "xs", fontWeight: "bold", children: "\u2713" }) }))] }) }, file.id));
|
|
4969
|
+
}) })) }))] }) }), jsx(DialogFooter, { children: jsxs(HStack, { gap: 3, justify: "end", children: [jsx(Button$1, { variant: "outline", onClick: handleClose, borderColor: "border.default", bg: "bg.panel", _hover: { bg: 'bg.muted' }, children: labels?.cancel ??
|
|
4970
|
+
translate(removeIndex(`${colLabel}.cancel`)) ??
|
|
4971
|
+
'Cancel' }), jsx(Button$1, { colorPalette: "blue", onClick: handleSelect, disabled: !selectedFileId, children: labels?.select ??
|
|
4972
|
+
translate(removeIndex(`${colLabel}.select`)) ??
|
|
4973
|
+
'Select' })] }) })] }) }));
|
|
4974
|
+
}
|
|
4666
4975
|
const FilePicker = ({ column, schema, prefix }) => {
|
|
4667
4976
|
const { setValue, formState: { errors }, watch, } = useFormContext();
|
|
4668
|
-
const {
|
|
4669
|
-
const
|
|
4977
|
+
const { filePickerLabels } = useSchemaContext();
|
|
4978
|
+
const formI18n = useFormI18n(column, prefix);
|
|
4979
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1', filePicker, } = schema;
|
|
4670
4980
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4671
|
-
const
|
|
4672
|
-
const
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4981
|
+
const currentValue = watch(column) ?? [];
|
|
4982
|
+
const currentFiles = Array.isArray(currentValue)
|
|
4983
|
+
? currentValue
|
|
4984
|
+
: [];
|
|
4985
|
+
const colLabel = formI18n.colLabel;
|
|
4986
|
+
const [dialogOpen, setDialogOpen] = useState(false);
|
|
4987
|
+
const { onFetchFiles, enableMediaLibrary = false, filterImageOnly = false, } = filePicker || {};
|
|
4988
|
+
const showMediaLibrary = enableMediaLibrary && !!onFetchFiles;
|
|
4989
|
+
const handleMediaLibrarySelect = (fileId) => {
|
|
4990
|
+
const newFiles = [...currentFiles, fileId];
|
|
4991
|
+
setValue(colLabel, newFiles);
|
|
4992
|
+
};
|
|
4993
|
+
const handleRemove = (index) => {
|
|
4994
|
+
const newFiles = currentFiles.filter((_, i) => i !== index);
|
|
4995
|
+
setValue(colLabel, newFiles);
|
|
4996
|
+
};
|
|
4997
|
+
const isFileObject = (value) => {
|
|
4998
|
+
return value instanceof File;
|
|
4999
|
+
};
|
|
5000
|
+
const getFileIdentifier = (file, index) => {
|
|
5001
|
+
if (isFileObject(file)) {
|
|
5002
|
+
return `${file.name}-${file.size}-${index}`;
|
|
5003
|
+
}
|
|
5004
|
+
return file;
|
|
5005
|
+
};
|
|
5006
|
+
const getFileName = (file) => {
|
|
5007
|
+
if (isFileObject(file)) {
|
|
5008
|
+
return file.name;
|
|
5009
|
+
}
|
|
5010
|
+
return typeof file === 'string' ? file : 'Unknown file';
|
|
5011
|
+
};
|
|
5012
|
+
const getFileSize = (file) => {
|
|
5013
|
+
if (isFileObject(file)) {
|
|
5014
|
+
return file.size;
|
|
5015
|
+
}
|
|
5016
|
+
return undefined;
|
|
5017
|
+
};
|
|
5018
|
+
const isImageFile = (file) => {
|
|
5019
|
+
if (isFileObject(file)) {
|
|
5020
|
+
return file.type.startsWith('image/');
|
|
5021
|
+
}
|
|
5022
|
+
if (typeof file === 'string') {
|
|
5023
|
+
return /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file);
|
|
5024
|
+
}
|
|
5025
|
+
return false;
|
|
5026
|
+
};
|
|
5027
|
+
const getImageUrl = (file) => {
|
|
5028
|
+
if (isFileObject(file)) {
|
|
5029
|
+
return URL.createObjectURL(file);
|
|
5030
|
+
}
|
|
5031
|
+
return undefined;
|
|
5032
|
+
};
|
|
5033
|
+
return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
5034
|
+
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [jsxs(VStack, { align: "stretch", gap: 2, children: [jsx(FileDropzone, { onDrop: ({ files }) => {
|
|
5035
|
+
const newFiles = files.filter(({ name }) => !currentFiles.some((cur) => {
|
|
5036
|
+
if (isFileObject(cur)) {
|
|
5037
|
+
return cur.name === name;
|
|
5038
|
+
}
|
|
5039
|
+
return false;
|
|
5040
|
+
}));
|
|
5041
|
+
setValue(colLabel, [...currentFiles, ...newFiles]);
|
|
5042
|
+
}, placeholder: filePickerLabels?.fileDropzone ?? formI18n.t('fileDropzone') }), showMediaLibrary && (jsx(Button$1, { variant: "outline", onClick: () => setDialogOpen(true), borderColor: "border.default", bg: "bg.panel", _hover: { bg: 'bg.muted' }, children: filePickerLabels?.browseLibrary ??
|
|
5043
|
+
formI18n.t('browse_library') ??
|
|
5044
|
+
'Browse from Library' }))] }), showMediaLibrary && (jsx(FilePickerDialog, { open: dialogOpen, onClose: () => setDialogOpen(false), onSelect: handleMediaLibrarySelect, title: filePickerLabels?.dialogTitle ??
|
|
5045
|
+
formI18n.t('dialog_title') ??
|
|
5046
|
+
'Select File', filterImageOnly: filterImageOnly, onFetchFiles: onFetchFiles, labels: filePickerLabels, translate: formI18n.t, colLabel: colLabel })), jsx(Flex, { flexFlow: 'column', gap: 1, children: currentFiles.map((file, index) => {
|
|
5047
|
+
const fileIdentifier = getFileIdentifier(file, index);
|
|
5048
|
+
const fileName = getFileName(file);
|
|
5049
|
+
const fileSize = getFileSize(file);
|
|
5050
|
+
const isImage = isImageFile(file);
|
|
5051
|
+
const imageUrl = getImageUrl(file);
|
|
5052
|
+
return (jsx(Card.Root, { variant: 'subtle', children: jsxs(Card.Body, { gap: "2", cursor: 'pointer', onClick: () => handleRemove(index), display: 'flex', flexFlow: 'row', alignItems: 'center', padding: '2', border: "2px solid", borderColor: "border.default", borderRadius: "md", _hover: {
|
|
5053
|
+
borderColor: 'blue.300',
|
|
5054
|
+
bg: 'bg.muted',
|
|
5055
|
+
}, transition: "all 0.2s", children: [jsx(Box, { width: "60px", height: "60px", display: "flex", alignItems: "center", justifyContent: "center", bg: "bg.muted", borderRadius: "md", flexShrink: 0, marginRight: "2", children: isImage && imageUrl ? (jsx(Image, { src: imageUrl, alt: fileName, boxSize: "60px", objectFit: "cover", borderRadius: "md" })) : (jsx(Icon, { as: LuFile, boxSize: 6, color: "fg.muted" })) }), jsxs(VStack, { align: "start", flex: 1, gap: 1, children: [jsx(Text, { fontSize: "sm", fontWeight: "medium", color: "fg.default", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: fileName }), fileSize !== undefined && (jsx(Text, { fontSize: "xs", color: "fg.muted", children: formatBytes(fileSize) }))] }), jsx(Icon, { as: TiDeleteOutline, boxSize: 5, color: "fg.muted" })] }) }, fileIdentifier));
|
|
5056
|
+
}) })] }));
|
|
4683
5057
|
};
|
|
4684
5058
|
|
|
4685
5059
|
const ToggleTip = React.forwardRef(function ToggleTip(props, ref) {
|
|
@@ -4868,7 +5242,7 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
4868
5242
|
return record[display_column];
|
|
4869
5243
|
};
|
|
4870
5244
|
return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
4871
|
-
gridRow, children: [isMultiple && (jsxs(Flex, { flexFlow: 'wrap', gap: 1, children: [watchIds.map((id) => {
|
|
5245
|
+
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [isMultiple && (jsxs(Flex, { flexFlow: 'wrap', gap: 1, children: [watchIds.map((id) => {
|
|
4872
5246
|
const item = idMap[id];
|
|
4873
5247
|
if (item === undefined) {
|
|
4874
5248
|
return (jsx(Text, { children: idPickerLabels?.undefined ?? formI18n.t('undefined') }, id));
|
|
@@ -4919,7 +5293,7 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
4919
5293
|
? idPickerLabels?.emptySearchResult ??
|
|
4920
5294
|
formI18n.t('empty_search_result')
|
|
4921
5295
|
: idPickerLabels?.initialResults ??
|
|
4922
|
-
formI18n.t('initial_results') })) }), jsx(PaginationRoot, { justifySelf: 'center', count: count, pageSize: limit, defaultPage: 1, page: page + 1, onPageChange: (e) => setPage(e.page - 1), children: jsxs(HStack, { gap: "4", children: [jsx(PaginationPrevTrigger, {}), count > 0 && jsx(PaginationPageText, {}), jsx(PaginationNextTrigger, {})] }) })] }))] }) })] })
|
|
5296
|
+
formI18n.t('initial_results') })) }), jsx(PaginationRoot, { justifySelf: 'center', count: count, pageSize: limit, defaultPage: 1, page: page + 1, onPageChange: (e) => setPage(e.page - 1), children: jsxs(HStack, { gap: "4", children: [jsx(PaginationPrevTrigger, {}), count > 0 && jsx(PaginationPageText, {}), jsx(PaginationNextTrigger, {})] }) })] }))] }) })] })] }));
|
|
4923
5297
|
};
|
|
4924
5298
|
|
|
4925
5299
|
const NumberInputRoot = React.forwardRef(function NumberInput$1(props, ref) {
|
|
@@ -4930,20 +5304,84 @@ const NumberInputField$1 = NumberInput.Input;
|
|
|
4930
5304
|
NumberInput.Scrubber;
|
|
4931
5305
|
NumberInput.Label;
|
|
4932
5306
|
|
|
5307
|
+
/**
|
|
5308
|
+
* Gets the error message for a specific field from react-hook-form errors
|
|
5309
|
+
* Prioritizes required errors (#.required) over field-specific validation errors
|
|
5310
|
+
*/
|
|
5311
|
+
const getFieldError = (errors, fieldName) => {
|
|
5312
|
+
// Check for form-level required errors first (highest priority)
|
|
5313
|
+
const requiredError = errors['#.required'];
|
|
5314
|
+
if (requiredError) {
|
|
5315
|
+
const requiredErrorMessage = extractErrorMessage(requiredError);
|
|
5316
|
+
if (requiredErrorMessage) {
|
|
5317
|
+
return requiredErrorMessage;
|
|
5318
|
+
}
|
|
5319
|
+
}
|
|
5320
|
+
// If no required errors, return field-specific error
|
|
5321
|
+
const fieldError = errors[fieldName];
|
|
5322
|
+
if (fieldError) {
|
|
5323
|
+
const fieldErrorMessage = extractErrorMessage(fieldError);
|
|
5324
|
+
if (fieldErrorMessage) {
|
|
5325
|
+
return fieldErrorMessage;
|
|
5326
|
+
}
|
|
5327
|
+
}
|
|
5328
|
+
return undefined;
|
|
5329
|
+
};
|
|
5330
|
+
/**
|
|
5331
|
+
* Helper function to extract error message from various error formats
|
|
5332
|
+
* Only returns message if explicitly provided, no fallback text
|
|
5333
|
+
*/
|
|
5334
|
+
const extractErrorMessage = (error) => {
|
|
5335
|
+
if (!error) {
|
|
5336
|
+
return undefined;
|
|
5337
|
+
}
|
|
5338
|
+
// If it's a simple string error
|
|
5339
|
+
if (typeof error === 'string') {
|
|
5340
|
+
return error;
|
|
5341
|
+
}
|
|
5342
|
+
// If it's an error object with a message property
|
|
5343
|
+
if (error && typeof error === 'object' && 'message' in error) {
|
|
5344
|
+
return error.message;
|
|
5345
|
+
}
|
|
5346
|
+
// If it's an array of errors, get the first one
|
|
5347
|
+
if (Array.isArray(error) && error.length > 0) {
|
|
5348
|
+
const firstError = error[0];
|
|
5349
|
+
if (typeof firstError === 'string') {
|
|
5350
|
+
return firstError;
|
|
5351
|
+
}
|
|
5352
|
+
if (firstError &&
|
|
5353
|
+
typeof firstError === 'object' &&
|
|
5354
|
+
'message' in firstError) {
|
|
5355
|
+
return firstError.message;
|
|
5356
|
+
}
|
|
5357
|
+
}
|
|
5358
|
+
// No fallback - return undefined if no message provided
|
|
5359
|
+
return undefined;
|
|
5360
|
+
};
|
|
5361
|
+
|
|
4933
5362
|
const NumberInputField = ({ schema, column, prefix, }) => {
|
|
4934
5363
|
const { setValue, formState: { errors }, watch, } = useFormContext();
|
|
4935
5364
|
const { translate } = useSchemaContext();
|
|
4936
|
-
const { required, gridColumn = 'span 12', gridRow = 'span 1' } = schema;
|
|
5365
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1', numberStorageType = 'number', } = schema;
|
|
4937
5366
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4938
5367
|
const colLabel = `${prefix}${column}`;
|
|
4939
5368
|
const value = watch(`${colLabel}`);
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
5369
|
+
const fieldError = getFieldError(errors, colLabel);
|
|
5370
|
+
return (jsx(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, gridColumn, gridRow, errorText: fieldError
|
|
5371
|
+
? fieldError.includes('required')
|
|
5372
|
+
? translate.t(removeIndex(`${colLabel}.field_required`))
|
|
5373
|
+
: fieldError
|
|
5374
|
+
: undefined, invalid: !!fieldError, children: jsx(NumberInputRoot, { value: value, onValueChange: (details) => {
|
|
5375
|
+
// Store as string or number based on configuration, default to number
|
|
5376
|
+
const value = numberStorageType === 'string'
|
|
5377
|
+
? details.value
|
|
5378
|
+
: details.valueAsNumber;
|
|
5379
|
+
setValue(`${colLabel}`, value);
|
|
5380
|
+
}, 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 }) }) }));
|
|
4943
5381
|
};
|
|
4944
5382
|
|
|
4945
5383
|
const ObjectInput = ({ schema, column, prefix }) => {
|
|
4946
|
-
const { properties, gridColumn =
|
|
5384
|
+
const { properties, gridColumn = 'span 12', gridRow = 'span 1', required, showLabel = true, } = schema;
|
|
4947
5385
|
const { translate } = useSchemaContext();
|
|
4948
5386
|
const colLabel = `${prefix}${column}`;
|
|
4949
5387
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
@@ -4951,29 +5389,32 @@ const ObjectInput = ({ schema, column, prefix }) => {
|
|
|
4951
5389
|
if (properties === undefined) {
|
|
4952
5390
|
throw new Error(`properties is undefined when using ObjectInput`);
|
|
4953
5391
|
}
|
|
4954
|
-
return (jsxs(Box, { gridRow, gridColumn, children: [showLabel && (jsxs(Box, { as: "label", children: [`${translate.t(removeIndex(`${colLabel}.field_label`))}`, isRequired && jsx("span", { children: "*" })] })), jsx(Grid, { bgColor: { base:
|
|
4955
|
-
base:
|
|
4956
|
-
_dark:
|
|
4957
|
-
}, gap: "4", padding:
|
|
5392
|
+
return (jsxs(Box, { gridRow, gridColumn, children: [showLabel && (jsxs(Box, { as: "label", children: [`${translate.t(removeIndex(`${colLabel}.field_label`))}`, isRequired && jsx("span", { children: "*" })] })), jsx(Grid, { bgColor: { base: 'colorPalette.100', _dark: 'colorPalette.900' }, p: 2, borderRadius: 4, borderWidth: 1, borderColor: {
|
|
5393
|
+
base: 'colorPalette.200',
|
|
5394
|
+
_dark: 'colorPalette.800',
|
|
5395
|
+
}, gap: "4", padding: '4', gridTemplateColumns: 'repeat(12, 1fr)', autoFlow: 'row', children: Object.keys(properties ?? {}).map((key) => {
|
|
4958
5396
|
return (
|
|
4959
5397
|
// @ts-expect-error find suitable types
|
|
4960
5398
|
jsx(ColumnRenderer, { column: `${key}`,
|
|
4961
5399
|
prefix: `${prefix}${column}.`,
|
|
4962
|
-
properties
|
|
4963
|
-
|
|
5400
|
+
properties,
|
|
5401
|
+
parentRequired: required }, `form-${colLabel}-${key}`));
|
|
5402
|
+
}) }), errors[`${column}`] && (jsx(Text, { color: 'red.400', children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
|
|
4964
5403
|
};
|
|
4965
5404
|
|
|
4966
5405
|
const RecordInput$1 = ({ column, schema, prefix }) => {
|
|
4967
5406
|
const { formState: { errors }, setValue, getValues, } = useFormContext();
|
|
4968
5407
|
const { translate } = useSchemaContext();
|
|
4969
|
-
const { required, gridColumn =
|
|
5408
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1' } = schema;
|
|
4970
5409
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4971
5410
|
const entries = Object.entries(getValues(column) ?? {});
|
|
4972
5411
|
const [showNewEntries, setShowNewEntries] = useState(false);
|
|
4973
5412
|
const [newKey, setNewKey] = useState();
|
|
4974
5413
|
const [newValue, setNewValue] = useState();
|
|
4975
|
-
return (jsxs(Field, { label: `${translate.t(`${column}.field_label`)}`, required: isRequired, alignItems:
|
|
4976
|
-
|
|
5414
|
+
return (jsxs(Field, { label: `${translate.t(`${column}.field_label`)}`, required: isRequired, alignItems: 'stretch', gridColumn, gridRow, errorText: errors[`${column}`]
|
|
5415
|
+
? translate.t(`${column}.field_required`)
|
|
5416
|
+
: undefined, invalid: !!errors[column], children: [entries.map(([key, value]) => {
|
|
5417
|
+
return (jsxs(Grid, { templateColumns: '1fr 1fr auto', gap: 1, children: [jsx(Input, { value: key, onChange: (e) => {
|
|
4977
5418
|
const filtered = entries.filter(([target]) => {
|
|
4978
5419
|
return target !== key;
|
|
4979
5420
|
});
|
|
@@ -4983,17 +5424,17 @@ const RecordInput$1 = ({ column, schema, prefix }) => {
|
|
|
4983
5424
|
...getValues(column),
|
|
4984
5425
|
[key]: e.target.value,
|
|
4985
5426
|
});
|
|
4986
|
-
}, autoComplete: "off" }), jsx(IconButton, { variant:
|
|
5427
|
+
}, autoComplete: "off" }), jsx(IconButton, { variant: 'ghost', onClick: () => {
|
|
4987
5428
|
const filtered = entries.filter(([target]) => {
|
|
4988
5429
|
return target !== key;
|
|
4989
5430
|
});
|
|
4990
5431
|
setValue(column, Object.fromEntries([...filtered]));
|
|
4991
5432
|
}, children: jsx(CgClose, {}) })] }));
|
|
4992
|
-
}), jsx(Show, { when: showNewEntries, children: jsxs(Card.Root, { children: [jsx(Card.Body, { gap: "2", children: jsxs(Grid, { templateColumns:
|
|
5433
|
+
}), jsx(Show, { when: showNewEntries, children: jsxs(Card.Root, { children: [jsx(Card.Body, { gap: "2", children: jsxs(Grid, { templateColumns: '1fr 1fr auto', gap: 1, children: [jsx(Input, { value: newKey, onChange: (e) => {
|
|
4993
5434
|
setNewKey(e.target.value);
|
|
4994
5435
|
}, autoComplete: "off" }), jsx(Input, { value: newValue, onChange: (e) => {
|
|
4995
5436
|
setNewValue(e.target.value);
|
|
4996
|
-
}, autoComplete: "off" })] }) }), jsxs(Card.Footer, { justifyContent: "flex-end", children: [jsx(IconButton, { variant:
|
|
5437
|
+
}, autoComplete: "off" })] }) }), jsxs(Card.Footer, { justifyContent: "flex-end", children: [jsx(IconButton, { variant: 'subtle', onClick: () => {
|
|
4997
5438
|
setShowNewEntries(false);
|
|
4998
5439
|
setNewKey(undefined);
|
|
4999
5440
|
setNewValue(undefined);
|
|
@@ -5012,16 +5453,17 @@ const RecordInput$1 = ({ column, schema, prefix }) => {
|
|
|
5012
5453
|
setShowNewEntries(true);
|
|
5013
5454
|
setNewKey(undefined);
|
|
5014
5455
|
setNewValue(undefined);
|
|
5015
|
-
}, children: translate.t(`${column}.addNew`) })
|
|
5456
|
+
}, children: translate.t(`${column}.addNew`) })] }));
|
|
5016
5457
|
};
|
|
5017
5458
|
|
|
5018
5459
|
const StringInputField = ({ column, schema, prefix, }) => {
|
|
5019
5460
|
const { register, formState: { errors }, } = useFormContext();
|
|
5020
5461
|
const { translate } = useSchemaContext();
|
|
5021
|
-
const { required, gridColumn =
|
|
5462
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1' } = schema;
|
|
5022
5463
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
5023
5464
|
const colLabel = `${prefix}${column}`;
|
|
5024
|
-
|
|
5465
|
+
const fieldError = getFieldError(errors, colLabel);
|
|
5466
|
+
return (jsx(Fragment, { children: jsx(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, gridColumn: gridColumn, gridRow: gridRow, errorText: fieldError, invalid: !!fieldError, children: jsx(Input, { ...register(`${colLabel}`, { required: isRequired }), autoComplete: "off" }) }) }));
|
|
5025
5467
|
};
|
|
5026
5468
|
|
|
5027
5469
|
const RadioCardItem = React.forwardRef(function RadioCardItem(props, ref) {
|
|
@@ -5207,13 +5649,18 @@ Textarea.displayName = "Textarea";
|
|
|
5207
5649
|
const TextAreaInput = ({ column, schema, prefix, }) => {
|
|
5208
5650
|
const { register, formState: { errors }, } = useFormContext();
|
|
5209
5651
|
const { translate } = useSchemaContext();
|
|
5210
|
-
const { required, gridColumn =
|
|
5652
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1' } = schema;
|
|
5211
5653
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
5212
5654
|
const colLabel = `${prefix}${column}`;
|
|
5213
5655
|
const form = useFormContext();
|
|
5214
5656
|
const { setValue, watch } = form;
|
|
5657
|
+
const fieldError = getFieldError(errors, colLabel);
|
|
5215
5658
|
const watchValue = watch(colLabel);
|
|
5216
|
-
return (jsx(Fragment, { children:
|
|
5659
|
+
return (jsx(Fragment, { children: jsx(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, gridColumn: gridColumn ?? 'span 4', gridRow: gridRow ?? 'span 1', display: "grid", errorText: fieldError
|
|
5660
|
+
? fieldError.includes('required')
|
|
5661
|
+
? translate.t(removeIndex(`${colLabel}.field_required`))
|
|
5662
|
+
: fieldError
|
|
5663
|
+
: undefined, invalid: !!fieldError, children: jsx(Textarea, { value: watchValue, onChange: (value) => setValue(colLabel, value) }) }) }));
|
|
5217
5664
|
};
|
|
5218
5665
|
|
|
5219
5666
|
function TimePicker$1({ hour, setHour, minute, setMinute, meridiem, setMeridiem, meridiemLabel = {
|
|
@@ -5344,25 +5791,25 @@ dayjs.extend(timezone);
|
|
|
5344
5791
|
const TimePicker = ({ column, schema, prefix }) => {
|
|
5345
5792
|
const { watch, formState: { errors }, setValue, } = useFormContext();
|
|
5346
5793
|
const { translate, timezone } = useSchemaContext();
|
|
5347
|
-
const { required, gridColumn =
|
|
5794
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1', timeFormat = 'HH:mm:ssZ', displayTimeFormat = 'hh:mm A', } = schema;
|
|
5348
5795
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
5349
5796
|
const colLabel = `${prefix}${column}`;
|
|
5350
5797
|
const [open, setOpen] = useState(false);
|
|
5351
5798
|
const value = watch(colLabel);
|
|
5352
5799
|
const displayedTime = dayjs(`1970-01-01T${value}`).tz(timezone).isValid()
|
|
5353
5800
|
? dayjs(`1970-01-01T${value}`).tz(timezone).format(displayTimeFormat)
|
|
5354
|
-
:
|
|
5801
|
+
: '';
|
|
5355
5802
|
// Parse the initial time parts from the time string (HH:mm:ssZ)
|
|
5356
5803
|
const parseTime = (time) => {
|
|
5357
5804
|
if (!time)
|
|
5358
|
-
return { hour: 12, minute: 0, meridiem:
|
|
5805
|
+
return { hour: 12, minute: 0, meridiem: 'am' };
|
|
5359
5806
|
const parsed = dayjs(`1970-01-01T${time}`).tz(timezone);
|
|
5360
5807
|
if (!parsed.isValid()) {
|
|
5361
|
-
return { hour: 12, minute: 0, meridiem:
|
|
5808
|
+
return { hour: 12, minute: 0, meridiem: 'am' };
|
|
5362
5809
|
}
|
|
5363
5810
|
let hour = parsed.hour();
|
|
5364
5811
|
const minute = parsed.minute();
|
|
5365
|
-
const meridiem = hour >= 12 ?
|
|
5812
|
+
const meridiem = hour >= 12 ? 'pm' : 'am';
|
|
5366
5813
|
if (hour === 0)
|
|
5367
5814
|
hour = 12;
|
|
5368
5815
|
else if (hour > 12)
|
|
@@ -5383,10 +5830,15 @@ const TimePicker = ({ column, schema, prefix }) => {
|
|
|
5383
5830
|
if (hour === null || minute === null || meridiem === null)
|
|
5384
5831
|
return null;
|
|
5385
5832
|
let newHour = hour;
|
|
5386
|
-
if (meridiem ===
|
|
5833
|
+
if (meridiem === 'pm' && hour !== 12) {
|
|
5387
5834
|
newHour = hour + 12;
|
|
5388
5835
|
}
|
|
5389
|
-
return dayjs()
|
|
5836
|
+
return dayjs()
|
|
5837
|
+
.tz(timezone)
|
|
5838
|
+
.hour(newHour)
|
|
5839
|
+
.minute(minute)
|
|
5840
|
+
.second(0)
|
|
5841
|
+
.format(timeFormat);
|
|
5390
5842
|
};
|
|
5391
5843
|
// Handle changes to time parts
|
|
5392
5844
|
const handleTimeChange = ({ hour: newHour, minute: newMinute, meridiem: newMeridiem, }) => {
|
|
@@ -5396,13 +5848,15 @@ const TimePicker = ({ column, schema, prefix }) => {
|
|
|
5396
5848
|
const timeString = getTimeString(newHour, newMinute, newMeridiem);
|
|
5397
5849
|
setValue(colLabel, timeString, { shouldValidate: true, shouldDirty: true });
|
|
5398
5850
|
};
|
|
5399
|
-
return (
|
|
5400
|
-
gridRow,
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5851
|
+
return (jsx(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, alignItems: 'stretch', gridColumn,
|
|
5852
|
+
gridRow, errorText: errors[`${colLabel}`]
|
|
5853
|
+
? translate.t(removeIndex(`${colLabel}.field_required`))
|
|
5854
|
+
: undefined, invalid: !!errors[colLabel], children: jsxs(Popover.Root, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, children: [jsx(Popover.Trigger, { asChild: true, children: jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
|
|
5855
|
+
setOpen(true);
|
|
5856
|
+
}, justifyContent: 'start', children: [jsx(IoMdClock, {}), !!value ? `${displayedTime}` : ''] }) }), jsx(Popover.Positioner, { children: jsx(Popover.Content, { children: jsx(Popover.Body, { children: jsx(TimePicker$1, { hour: hour, setHour: setHour, minute: minute, setMinute: setMinute, meridiem: meridiem, setMeridiem: setMeridiem, onChange: handleTimeChange, meridiemLabel: {
|
|
5857
|
+
am: translate.t(`common.am`, { defaultValue: 'AM' }),
|
|
5858
|
+
pm: translate.t(`common.pm`, { defaultValue: 'PM' }),
|
|
5859
|
+
} }) }) }) })] }) }));
|
|
5406
5860
|
};
|
|
5407
5861
|
|
|
5408
5862
|
function IsoTimePicker({ hour, setHour, minute, setMinute, second, setSecond, onChange = (_newValue) => { }, }) {
|
|
@@ -5608,9 +6062,9 @@ const DateTimePicker = ({ column, schema, prefix, }) => {
|
|
|
5608
6062
|
const { watch, formState: { errors }, setValue, } = useFormContext();
|
|
5609
6063
|
const { timezone, dateTimePickerLabels } = useSchemaContext();
|
|
5610
6064
|
const formI18n = useFormI18n(column, prefix);
|
|
5611
|
-
const { required, gridColumn =
|
|
6065
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1', displayDateFormat = 'YYYY-MM-DD HH:mm:ss',
|
|
5612
6066
|
// with timezone
|
|
5613
|
-
dateFormat =
|
|
6067
|
+
dateFormat = 'YYYY-MM-DD[T]HH:mm:ssZ', } = schema;
|
|
5614
6068
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
5615
6069
|
const colLabel = formI18n.colLabel;
|
|
5616
6070
|
const [open, setOpen] = useState(false);
|
|
@@ -5641,102 +6095,143 @@ const DateTimePicker = ({ column, schema, prefix, }) => {
|
|
|
5641
6095
|
console.error(e);
|
|
5642
6096
|
}
|
|
5643
6097
|
}, [selectedDate, dateFormat, colLabel, setValue]);
|
|
5644
|
-
return (
|
|
5645
|
-
gridRow, children:
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
defaultValue:
|
|
6098
|
+
return (jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
6099
|
+
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], 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: () => {
|
|
6100
|
+
setOpen(true);
|
|
6101
|
+
}, justifyContent: 'start', children: [jsx(MdDateRange, {}), selectedDate !== undefined ? `${displayDate}` : ''] }) }), jsx(PopoverContent, { minW: '450px', children: jsxs(PopoverBody, { children: [jsx(PopoverTitle, {}), jsx(DateTimePicker$1, { value: selectedDate, onChange: (date) => {
|
|
6102
|
+
setValue(colLabel, dayjs(date).tz(timezone).format(dateFormat));
|
|
6103
|
+
}, timezone: timezone, labels: {
|
|
6104
|
+
monthNamesShort: dateTimePickerLabels?.monthNamesShort ?? [
|
|
6105
|
+
formI18n.translate.t(`common.month_1`, {
|
|
6106
|
+
defaultValue: 'January',
|
|
6107
|
+
}),
|
|
6108
|
+
formI18n.translate.t(`common.month_2`, {
|
|
6109
|
+
defaultValue: 'February',
|
|
6110
|
+
}),
|
|
6111
|
+
formI18n.translate.t(`common.month_3`, {
|
|
6112
|
+
defaultValue: 'March',
|
|
6113
|
+
}),
|
|
6114
|
+
formI18n.translate.t(`common.month_4`, {
|
|
6115
|
+
defaultValue: 'April',
|
|
6116
|
+
}),
|
|
6117
|
+
formI18n.translate.t(`common.month_5`, {
|
|
6118
|
+
defaultValue: 'May',
|
|
6119
|
+
}),
|
|
6120
|
+
formI18n.translate.t(`common.month_6`, {
|
|
6121
|
+
defaultValue: 'June',
|
|
6122
|
+
}),
|
|
6123
|
+
formI18n.translate.t(`common.month_7`, {
|
|
6124
|
+
defaultValue: 'July',
|
|
6125
|
+
}),
|
|
6126
|
+
formI18n.translate.t(`common.month_8`, {
|
|
6127
|
+
defaultValue: 'August',
|
|
6128
|
+
}),
|
|
6129
|
+
formI18n.translate.t(`common.month_9`, {
|
|
6130
|
+
defaultValue: 'September',
|
|
6131
|
+
}),
|
|
6132
|
+
formI18n.translate.t(`common.month_10`, {
|
|
6133
|
+
defaultValue: 'October',
|
|
5677
6134
|
}),
|
|
5678
|
-
|
|
5679
|
-
defaultValue:
|
|
6135
|
+
formI18n.translate.t(`common.month_11`, {
|
|
6136
|
+
defaultValue: 'November',
|
|
5680
6137
|
}),
|
|
5681
|
-
|
|
6138
|
+
formI18n.translate.t(`common.month_12`, {
|
|
6139
|
+
defaultValue: 'December',
|
|
6140
|
+
}),
|
|
6141
|
+
],
|
|
6142
|
+
weekdayNamesShort: dateTimePickerLabels?.weekdayNamesShort ?? [
|
|
6143
|
+
formI18n.translate.t(`common.weekday_1`, {
|
|
6144
|
+
defaultValue: 'Sun',
|
|
6145
|
+
}),
|
|
6146
|
+
formI18n.translate.t(`common.weekday_2`, {
|
|
6147
|
+
defaultValue: 'Mon',
|
|
6148
|
+
}),
|
|
6149
|
+
formI18n.translate.t(`common.weekday_3`, {
|
|
6150
|
+
defaultValue: 'Tue',
|
|
6151
|
+
}),
|
|
6152
|
+
formI18n.translate.t(`common.weekday_4`, {
|
|
6153
|
+
defaultValue: 'Wed',
|
|
6154
|
+
}),
|
|
6155
|
+
formI18n.translate.t(`common.weekday_5`, {
|
|
6156
|
+
defaultValue: 'Thu',
|
|
6157
|
+
}),
|
|
6158
|
+
formI18n.translate.t(`common.weekday_6`, {
|
|
6159
|
+
defaultValue: 'Fri',
|
|
6160
|
+
}),
|
|
6161
|
+
formI18n.translate.t(`common.weekday_7`, {
|
|
6162
|
+
defaultValue: 'Sat',
|
|
6163
|
+
}),
|
|
6164
|
+
],
|
|
6165
|
+
backButtonLabel: dateTimePickerLabels?.backButtonLabel ??
|
|
6166
|
+
formI18n.translate.t(`common.back_button`, {
|
|
6167
|
+
defaultValue: 'Back',
|
|
6168
|
+
}),
|
|
6169
|
+
forwardButtonLabel: dateTimePickerLabels?.forwardButtonLabel ??
|
|
6170
|
+
formI18n.translate.t(`common.forward_button`, {
|
|
6171
|
+
defaultValue: 'Forward',
|
|
6172
|
+
}),
|
|
6173
|
+
} })] }) })] }) }));
|
|
5682
6174
|
};
|
|
5683
6175
|
|
|
5684
6176
|
const SchemaRenderer = ({ schema, prefix, column, }) => {
|
|
5685
6177
|
const colSchema = schema;
|
|
5686
6178
|
const { type, variant, properties: innerProperties, foreign_key, format, items, } = schema;
|
|
5687
|
-
if (variant ===
|
|
6179
|
+
if (variant === 'custom-input') {
|
|
5688
6180
|
return jsx(CustomInput, { schema: colSchema, prefix, column });
|
|
5689
6181
|
}
|
|
5690
|
-
if (type ===
|
|
6182
|
+
if (type === 'string') {
|
|
5691
6183
|
if ((schema.enum ?? []).length > 0) {
|
|
5692
6184
|
return jsx(EnumPicker, { schema: colSchema, prefix, column });
|
|
5693
6185
|
}
|
|
5694
|
-
if (variant ===
|
|
6186
|
+
if (variant === 'id-picker') {
|
|
5695
6187
|
idPickerSanityCheck(column, foreign_key);
|
|
5696
6188
|
return jsx(IdPicker, { schema: colSchema, prefix, column });
|
|
5697
6189
|
}
|
|
5698
|
-
if (format ===
|
|
6190
|
+
if (format === 'date') {
|
|
5699
6191
|
return jsx(DatePicker, { schema: colSchema, prefix, column });
|
|
5700
6192
|
}
|
|
5701
|
-
if (format ===
|
|
6193
|
+
if (format === 'time') {
|
|
5702
6194
|
return jsx(TimePicker, { schema: colSchema, prefix, column });
|
|
5703
6195
|
}
|
|
5704
|
-
if (format ===
|
|
6196
|
+
if (format === 'date-time') {
|
|
5705
6197
|
return jsx(DateTimePicker, { schema: colSchema, prefix, column });
|
|
5706
6198
|
}
|
|
5707
|
-
if (variant ===
|
|
6199
|
+
if (variant === 'text-area') {
|
|
5708
6200
|
return jsx(TextAreaInput, { schema: colSchema, prefix, column });
|
|
5709
6201
|
}
|
|
5710
6202
|
return jsx(StringInputField, { schema: colSchema, prefix, column });
|
|
5711
6203
|
}
|
|
5712
|
-
if (type ===
|
|
6204
|
+
if (type === 'number' || type === 'integer') {
|
|
5713
6205
|
return jsx(NumberInputField, { schema: colSchema, prefix, column });
|
|
5714
6206
|
}
|
|
5715
|
-
if (type ===
|
|
6207
|
+
if (type === 'boolean') {
|
|
5716
6208
|
return jsx(BooleanPicker, { schema: colSchema, prefix, column });
|
|
5717
6209
|
}
|
|
5718
|
-
if (type ===
|
|
6210
|
+
if (type === 'object') {
|
|
5719
6211
|
if (innerProperties) {
|
|
5720
6212
|
return jsx(ObjectInput, { schema: colSchema, prefix, column });
|
|
5721
6213
|
}
|
|
5722
6214
|
return jsx(RecordInput$1, { schema: colSchema, prefix, column });
|
|
5723
6215
|
}
|
|
5724
|
-
if (type ===
|
|
5725
|
-
if (variant ===
|
|
6216
|
+
if (type === 'array') {
|
|
6217
|
+
if (variant === 'id-picker') {
|
|
5726
6218
|
idPickerSanityCheck(column, foreign_key);
|
|
5727
6219
|
return (jsx(IdPicker, { schema: colSchema, prefix, column, isMultiple: true }));
|
|
5728
6220
|
}
|
|
5729
|
-
if (variant ===
|
|
6221
|
+
if (variant === 'tag-picker') {
|
|
5730
6222
|
return jsx(TagPicker, { schema: colSchema, prefix, column });
|
|
5731
6223
|
}
|
|
5732
|
-
if (variant ===
|
|
6224
|
+
if (variant === 'file-picker') {
|
|
5733
6225
|
return jsx(FilePicker, { schema: colSchema, prefix, column });
|
|
5734
6226
|
}
|
|
5735
|
-
if (variant ===
|
|
6227
|
+
if (variant === 'date-range') {
|
|
6228
|
+
return jsx(DateRangePicker, { schema: colSchema, prefix, column });
|
|
6229
|
+
}
|
|
6230
|
+
if (variant === 'enum-picker') {
|
|
5736
6231
|
const { items } = colSchema;
|
|
5737
6232
|
const { enum: enumItems } = items;
|
|
5738
6233
|
const enumSchema = {
|
|
5739
|
-
type:
|
|
6234
|
+
type: 'string',
|
|
5740
6235
|
enum: enumItems,
|
|
5741
6236
|
};
|
|
5742
6237
|
return (jsx(EnumPicker, { isMultiple: true, schema: enumSchema, prefix, column }));
|
|
@@ -5746,19 +6241,24 @@ const SchemaRenderer = ({ schema, prefix, column, }) => {
|
|
|
5746
6241
|
}
|
|
5747
6242
|
return jsx(Text, { children: `array ${column}` });
|
|
5748
6243
|
}
|
|
5749
|
-
if (type ===
|
|
6244
|
+
if (type === 'null') {
|
|
5750
6245
|
return jsx(Text, { children: `null ${column}` });
|
|
5751
6246
|
}
|
|
5752
6247
|
return jsx(Text, { children: "missing type" });
|
|
5753
6248
|
};
|
|
5754
6249
|
|
|
5755
|
-
const ColumnRenderer = ({ column, properties, prefix, }) => {
|
|
6250
|
+
const ColumnRenderer = ({ column, properties, prefix, parentRequired, }) => {
|
|
5756
6251
|
const colSchema = properties[column];
|
|
5757
6252
|
const colLabel = `${prefix}${column}`;
|
|
5758
6253
|
if (colSchema === undefined) {
|
|
5759
6254
|
throw new Error(`${colLabel} does not exist when using ColumnRenderer`);
|
|
5760
6255
|
}
|
|
5761
|
-
|
|
6256
|
+
// Merge parent's required array with the schema's required array
|
|
6257
|
+
const schemaWithRequired = {
|
|
6258
|
+
...colSchema,
|
|
6259
|
+
required: parentRequired || colSchema.required,
|
|
6260
|
+
};
|
|
6261
|
+
return jsx(SchemaRenderer, { schema: schemaWithRequired, prefix, column });
|
|
5762
6262
|
};
|
|
5763
6263
|
|
|
5764
6264
|
const ArrayViewer = ({ schema, column, prefix }) => {
|
|
@@ -6199,15 +6699,15 @@ const SubmitButton = () => {
|
|
|
6199
6699
|
const methods = useFormContext();
|
|
6200
6700
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6201
6701
|
const onValid = (data) => {
|
|
6202
|
-
const { isValid, errors } = validateData(data, schema);
|
|
6203
|
-
if (!isValid) {
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
}
|
|
6702
|
+
// const { isValid, errors } = validateData(data, schema);
|
|
6703
|
+
// if (!isValid) {
|
|
6704
|
+
// setError({
|
|
6705
|
+
// type: 'validation',
|
|
6706
|
+
// errors,
|
|
6707
|
+
// });
|
|
6708
|
+
// setIsError(true);
|
|
6709
|
+
// return;
|
|
6710
|
+
// }
|
|
6211
6711
|
// If validation passes, check if confirmation is required
|
|
6212
6712
|
if (requireConfirmation) {
|
|
6213
6713
|
// Show confirmation (existing behavior)
|
|
@@ -6232,10 +6732,6 @@ const FormBody = () => {
|
|
|
6232
6732
|
const { showSubmitButton, showResetButton } = displayConfig;
|
|
6233
6733
|
const methods = useFormContext();
|
|
6234
6734
|
const { properties } = schema;
|
|
6235
|
-
// Custom error renderer for validation errors with i18n support
|
|
6236
|
-
const renderValidationErrors = (validationErrors) => {
|
|
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))) }));
|
|
6238
|
-
};
|
|
6239
6735
|
const renderColumns = ({ order, keys, ignore, include, }) => {
|
|
6240
6736
|
const included = include.length > 0 ? include : keys;
|
|
6241
6737
|
const not_exist = included.filter((columnA) => !order.some((columnB) => columnA === columnB));
|
|
@@ -6275,19 +6771,17 @@ const FormBody = () => {
|
|
|
6275
6771
|
setIsConfirming(false);
|
|
6276
6772
|
}, variant: 'subtle', children: translate.t('cancel') }), jsx(Button$1, { onClick: () => {
|
|
6277
6773
|
onFormSubmit(validatedData);
|
|
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 &&
|
|
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)}` })] }) }) })] })] })) })) }))] }));
|
|
6774
|
+
}, 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 && customErrorRenderer && customErrorRenderer(error)] }));
|
|
6280
6775
|
}
|
|
6281
6776
|
return (jsxs(Flex, { flexFlow: 'column', gap: "2", children: [jsx(Grid, { gap: "4", gridTemplateColumns: 'repeat(12, 1fr)', autoFlow: 'row', children: ordered.map((column) => {
|
|
6282
6777
|
return (jsx(ColumnRenderer
|
|
6283
6778
|
// @ts-expect-error find suitable types
|
|
6284
6779
|
, {
|
|
6285
6780
|
// @ts-expect-error find suitable types
|
|
6286
|
-
properties: properties, prefix: ``, column }, `form-input-${column}`));
|
|
6781
|
+
properties: properties, prefix: ``, parentRequired: schema.required, column }, `form-input-${column}`));
|
|
6287
6782
|
}) }), jsxs(Flex, { justifyContent: 'end', gap: "2", children: [showResetButton && (jsx(Button$1, { onClick: () => {
|
|
6288
6783
|
methods.reset();
|
|
6289
|
-
}, variant: 'subtle', children: translate.t('reset') })), showSubmitButton && jsx(SubmitButton, {})] }), isError &&
|
|
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)}` })] }) }) })] })] })) })) }))] }));
|
|
6784
|
+
}, variant: 'subtle', children: translate.t('reset') })), showSubmitButton && jsx(SubmitButton, {})] }), isError && customErrorRenderer && customErrorRenderer(error)] }));
|
|
6291
6785
|
};
|
|
6292
6786
|
|
|
6293
6787
|
const FormTitle = () => {
|
|
@@ -6300,12 +6794,15 @@ const DefaultForm = ({ formConfig, }) => {
|
|
|
6300
6794
|
return (jsx(FormRoot, { ...formConfig, children: jsxs(Grid, { gap: "2", children: [showTitle && jsx(FormTitle, {}), jsx(FormBody, {})] }) }));
|
|
6301
6795
|
};
|
|
6302
6796
|
|
|
6303
|
-
const useForm = ({ preLoadedValues, keyPrefix, namespace }) => {
|
|
6797
|
+
const useForm = ({ preLoadedValues, keyPrefix, namespace, schema, }) => {
|
|
6304
6798
|
const form = useForm$1({
|
|
6305
6799
|
values: preLoadedValues,
|
|
6800
|
+
resolver: schema ? ajvResolver(schema) : undefined,
|
|
6801
|
+
mode: 'onBlur',
|
|
6802
|
+
reValidateMode: 'onBlur',
|
|
6306
6803
|
});
|
|
6307
6804
|
const [idMap, setIdMap] = useState({});
|
|
6308
|
-
const translate = useTranslation(namespace ||
|
|
6805
|
+
const translate = useTranslation(namespace || '', { keyPrefix });
|
|
6309
6806
|
return {
|
|
6310
6807
|
form,
|
|
6311
6808
|
idMap,
|
|
@@ -6375,15 +6872,15 @@ const buildErrorMessages = (config) => {
|
|
|
6375
6872
|
}
|
|
6376
6873
|
// Add global fallback error messages
|
|
6377
6874
|
const globalKeys = [
|
|
6378
|
-
|
|
6379
|
-
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
|
|
6383
|
-
|
|
6384
|
-
|
|
6385
|
-
|
|
6386
|
-
|
|
6875
|
+
'minLength',
|
|
6876
|
+
'maxLength',
|
|
6877
|
+
'pattern',
|
|
6878
|
+
'minimum',
|
|
6879
|
+
'maximum',
|
|
6880
|
+
'multipleOf',
|
|
6881
|
+
'format',
|
|
6882
|
+
'type',
|
|
6883
|
+
'enum',
|
|
6387
6884
|
];
|
|
6388
6885
|
globalKeys.forEach((key) => {
|
|
6389
6886
|
if (config[key]) {
|
|
@@ -6392,6 +6889,46 @@ const buildErrorMessages = (config) => {
|
|
|
6392
6889
|
});
|
|
6393
6890
|
return result;
|
|
6394
6891
|
};
|
|
6892
|
+
/**
|
|
6893
|
+
* Converts buildErrorMessages result to ajv-errors compatible format
|
|
6894
|
+
*/
|
|
6895
|
+
const convertToAjvErrorsFormat = (errorMessages) => {
|
|
6896
|
+
const result = {};
|
|
6897
|
+
// Convert required field errors
|
|
6898
|
+
if (errorMessages.required) {
|
|
6899
|
+
result.required = errorMessages.required;
|
|
6900
|
+
}
|
|
6901
|
+
// Convert properties errors to ajv-errors format
|
|
6902
|
+
if (errorMessages.properties) {
|
|
6903
|
+
result.properties = {};
|
|
6904
|
+
Object.keys(errorMessages.properties).forEach((fieldName) => {
|
|
6905
|
+
const fieldErrors = errorMessages.properties[fieldName];
|
|
6906
|
+
result.properties[fieldName] = {};
|
|
6907
|
+
Object.keys(fieldErrors).forEach((keyword) => {
|
|
6908
|
+
result.properties[fieldName][keyword] =
|
|
6909
|
+
fieldErrors[keyword];
|
|
6910
|
+
});
|
|
6911
|
+
});
|
|
6912
|
+
}
|
|
6913
|
+
// Add global fallback errors
|
|
6914
|
+
const globalKeys = [
|
|
6915
|
+
'minLength',
|
|
6916
|
+
'maxLength',
|
|
6917
|
+
'pattern',
|
|
6918
|
+
'minimum',
|
|
6919
|
+
'maximum',
|
|
6920
|
+
'multipleOf',
|
|
6921
|
+
'format',
|
|
6922
|
+
'type',
|
|
6923
|
+
'enum',
|
|
6924
|
+
];
|
|
6925
|
+
globalKeys.forEach((key) => {
|
|
6926
|
+
if (errorMessages[key]) {
|
|
6927
|
+
result[key] = errorMessages[key];
|
|
6928
|
+
}
|
|
6929
|
+
});
|
|
6930
|
+
return result;
|
|
6931
|
+
};
|
|
6395
6932
|
/**
|
|
6396
6933
|
* Helper function to build required field errors
|
|
6397
6934
|
*
|
|
@@ -6434,10 +6971,10 @@ const buildErrorMessages = (config) => {
|
|
|
6434
6971
|
* // Result: { username: "user.username.field_required", email: "user.email.field_required" }
|
|
6435
6972
|
* ```
|
|
6436
6973
|
*/
|
|
6437
|
-
const buildRequiredErrors = (fields, messageOrGenerator, keyPrefix =
|
|
6974
|
+
const buildRequiredErrors = (fields, messageOrGenerator, keyPrefix = '') => {
|
|
6438
6975
|
const result = {};
|
|
6439
6976
|
fields.forEach((field) => {
|
|
6440
|
-
if (typeof messageOrGenerator ===
|
|
6977
|
+
if (typeof messageOrGenerator === 'function') {
|
|
6441
6978
|
const message = messageOrGenerator(field);
|
|
6442
6979
|
result[field] = keyPrefix ? `${keyPrefix}.${message}` : message;
|
|
6443
6980
|
}
|
|
@@ -6508,11 +7045,14 @@ const buildFieldErrors = (config) => {
|
|
|
6508
7045
|
* ```
|
|
6509
7046
|
*/
|
|
6510
7047
|
const createErrorMessage = (required, properties, globalFallbacks) => {
|
|
6511
|
-
|
|
7048
|
+
const config = {
|
|
6512
7049
|
required,
|
|
6513
7050
|
properties,
|
|
6514
|
-
|
|
6515
|
-
|
|
7051
|
+
};
|
|
7052
|
+
if (globalFallbacks) {
|
|
7053
|
+
Object.assign(config, globalFallbacks);
|
|
7054
|
+
}
|
|
7055
|
+
return buildErrorMessages(config);
|
|
6516
7056
|
};
|
|
6517
7057
|
|
|
6518
7058
|
const getMultiDates = ({ selected, selectedDate, selectedDates, selectable, }) => {
|
|
@@ -6531,4 +7071,4 @@ const getMultiDates = ({ selected, selectedDate, selectedDates, selectable, }) =
|
|
|
6531
7071
|
}
|
|
6532
7072
|
};
|
|
6533
7073
|
|
|
6534
|
-
export { CardHeader, DataDisplay, DataTable, DataTableServer, DefaultCardTitle, DefaultForm, DefaultTable, DensityToggleButton, EditSortingButton, EmptyState$1 as EmptyState, ErrorAlert, FilterDialog, FormBody, FormRoot, FormTitle, GlobalFilter, PageSizeControl, Pagination, RecordDisplay, ReloadButton, ResetFilteringButton, ResetSelectionButton, ResetSortingButton, RowCountText, Table, TableBody, TableCardContainer, TableCards, TableComponent, TableControls, TableDataDisplay, TableFilter, TableFilterTags, TableFooter, TableHeader, TableLoadingComponent, TableSelector, TableSorter, TableViewer, TextCell, ViewDialog, buildErrorMessages, buildFieldErrors, buildRequiredErrors, createErrorMessage, getColumns, getMultiDates, getRangeDates, idPickerSanityCheck, useDataTable, useDataTableContext, useDataTableServer, useForm, widthSanityCheck };
|
|
7074
|
+
export { CardHeader, DataDisplay, DataTable, DataTableServer, DefaultCardTitle, DefaultForm, DefaultTable, DensityToggleButton, EditSortingButton, EmptyState$1 as EmptyState, ErrorAlert, FilterDialog, FormBody, FormRoot, FormTitle, GlobalFilter, PageSizeControl, Pagination, RecordDisplay, ReloadButton, ResetFilteringButton, ResetSelectionButton, ResetSortingButton, RowCountText, Table, TableBody, TableCardContainer, TableCards, TableComponent, TableControls, TableDataDisplay, TableFilter, TableFilterTags, TableFooter, TableHeader, TableLoadingComponent, TableSelector, TableSorter, TableViewer, TextCell, ViewDialog, buildErrorMessages, buildFieldErrors, buildRequiredErrors, convertToAjvErrorsFormat, createErrorMessage, getColumns, getMultiDates, getRangeDates, idPickerSanityCheck, useDataTable, useDataTableContext, useDataTableServer, useForm, widthSanityCheck };
|