@bsol-oss/react-datatable5 12.0.0-beta.59 → 12.0.0-beta.60
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 +2 -53
- package/dist/index.js +34 -229
- package/dist/index.mjs +36 -227
- package/dist/types/components/Form/SchemaFormContext.d.ts +0 -2
- package/dist/types/components/Form/components/core/FormRoot.d.ts +1 -3
- package/dist/types/components/Form/utils/validation.d.ts +62 -7
- package/dist/types/index.d.ts +0 -2
- package/package.json +4 -3
package/dist/index.d.ts
CHANGED
|
@@ -11,12 +11,10 @@ import { RankingInfo } from '@tanstack/match-sorter-utils';
|
|
|
11
11
|
import { UseQueryResult } from '@tanstack/react-query';
|
|
12
12
|
import { JSONSchema7 } from 'json-schema';
|
|
13
13
|
import { ForeignKeyProps as ForeignKeyProps$1 } from '@/components/Form/components/fields/StringInputField';
|
|
14
|
-
import { SupportedLocale as SupportedLocale$1 } from '@/components/Form/utils/validation';
|
|
15
14
|
import { AxiosRequestConfig } from 'axios';
|
|
16
15
|
import * as react_hook_form from 'react-hook-form';
|
|
17
16
|
import { UseFormReturn, FieldValues, SubmitHandler } from 'react-hook-form';
|
|
18
17
|
import { RenderProps, Props } from '@bsol-oss/dayzed-react19';
|
|
19
|
-
import * as ajv_i18n_localize_types from 'ajv-i18n/localize/types';
|
|
20
18
|
|
|
21
19
|
interface DensityToggleButtonProps {
|
|
22
20
|
icon?: React__default.ReactElement;
|
|
@@ -549,7 +547,6 @@ interface FormRootProps<TData extends FieldValues> {
|
|
|
549
547
|
requestOptions?: AxiosRequestConfig;
|
|
550
548
|
getUpdatedData?: () => TData | Promise<TData> | void;
|
|
551
549
|
customErrorRenderer?: (error: unknown) => ReactNode;
|
|
552
|
-
validationLocale?: SupportedLocale$1;
|
|
553
550
|
}
|
|
554
551
|
interface CustomJSONSchema7Definition extends JSONSchema7 {
|
|
555
552
|
variant: string;
|
|
@@ -566,7 +563,7 @@ declare const idPickerSanityCheck: (column: string, foreign_key?: {
|
|
|
566
563
|
column?: string | undefined;
|
|
567
564
|
display_column?: string | undefined;
|
|
568
565
|
} | undefined) => void;
|
|
569
|
-
declare const FormRoot: <TData extends FieldValues>({ schema, idMap, setIdMap, form, serverUrl, translate, children, order, ignore, include, onSubmit, rowNumber, requestOptions, getUpdatedData, customErrorRenderer,
|
|
566
|
+
declare const FormRoot: <TData extends FieldValues>({ schema, idMap, setIdMap, form, serverUrl, translate, children, order, ignore, include, onSubmit, rowNumber, requestOptions, getUpdatedData, customErrorRenderer, }: FormRootProps<TData>) => react_jsx_runtime.JSX.Element;
|
|
570
567
|
|
|
571
568
|
interface DefaultFormProps<TData extends FieldValues> {
|
|
572
569
|
formConfig: Omit<FormRootProps<TData>, "children">;
|
|
@@ -664,54 +661,6 @@ interface RecordDisplayProps {
|
|
|
664
661
|
}
|
|
665
662
|
declare const RecordDisplay: ({ object, boxProps, translate, prefix, }: RecordDisplayProps) => react_jsx_runtime.JSX.Element;
|
|
666
663
|
|
|
667
|
-
declare const localize: {
|
|
668
|
-
en: () => void;
|
|
669
|
-
'zh-HK': ajv_i18n_localize_types.Localize;
|
|
670
|
-
'zh-TW': ajv_i18n_localize_types.Localize;
|
|
671
|
-
'zh-CN': ajv_i18n_localize_types.Localize;
|
|
672
|
-
zh: ajv_i18n_localize_types.Localize;
|
|
673
|
-
};
|
|
674
|
-
type SupportedLocale = keyof typeof localize;
|
|
675
|
-
interface ValidationError {
|
|
676
|
-
field: string;
|
|
677
|
-
message: string;
|
|
678
|
-
value?: unknown;
|
|
679
|
-
schemaPath?: string;
|
|
680
|
-
}
|
|
681
|
-
interface ValidationResult {
|
|
682
|
-
isValid: boolean;
|
|
683
|
-
errors: ValidationError[];
|
|
684
|
-
}
|
|
685
|
-
interface ValidationOptions {
|
|
686
|
-
locale?: SupportedLocale;
|
|
687
|
-
}
|
|
688
|
-
/**
|
|
689
|
-
* Validates data against a JSON Schema using AJV with i18n support
|
|
690
|
-
* @param data - The data to validate
|
|
691
|
-
* @param schema - The JSON Schema to validate against
|
|
692
|
-
* @param options - Validation options including locale
|
|
693
|
-
* @returns ValidationResult containing validation status and errors
|
|
694
|
-
*/
|
|
695
|
-
declare const validateData: (data: unknown, schema: JSONSchema7, options?: ValidationOptions) => ValidationResult;
|
|
696
|
-
/**
|
|
697
|
-
* Creates a reusable validator function for a specific schema with i18n support
|
|
698
|
-
* @param schema - The JSON Schema to create validator for
|
|
699
|
-
* @param locale - The locale to use for error messages
|
|
700
|
-
* @returns A function that validates data against the schema
|
|
701
|
-
*/
|
|
702
|
-
declare const createSchemaValidator: (schema: JSONSchema7, locale?: SupportedLocale) => (data: unknown) => ValidationResult;
|
|
703
|
-
/**
|
|
704
|
-
* Get available locales for validation error messages
|
|
705
|
-
* @returns Array of supported locale codes
|
|
706
|
-
*/
|
|
707
|
-
declare const getSupportedLocales: () => SupportedLocale[];
|
|
708
|
-
/**
|
|
709
|
-
* Check if a locale is supported
|
|
710
|
-
* @param locale - The locale to check
|
|
711
|
-
* @returns Boolean indicating if the locale is supported
|
|
712
|
-
*/
|
|
713
|
-
declare const isLocaleSupported: (locale: string) => locale is "en" | "zh-HK" | "zh-TW" | "zh-CN" | "zh";
|
|
714
|
-
|
|
715
664
|
declare module "@tanstack/react-table" {
|
|
716
665
|
interface ColumnMeta<TData extends RowData, TValue> {
|
|
717
666
|
/**
|
|
@@ -784,4 +733,4 @@ declare module "@tanstack/react-table" {
|
|
|
784
733
|
}
|
|
785
734
|
}
|
|
786
735
|
|
|
787
|
-
export { type CalendarProps, CardHeader, type CardHeaderProps, type CustomJSONSchema7Definition, DataDisplay, type DataDisplayProps, type DataResponse, DataTable, type DataTableDefaultState, type DataTableProps, DataTableServer, type DataTableServerProps, type DatePickerLabels, type DatePickerProps, DefaultCardTitle, DefaultForm, type DefaultFormProps, DefaultTable, type DefaultTableProps, DensityToggleButton, type DensityToggleButtonProps, type EditFilterButtonProps, EditSortingButton, type EditSortingButtonProps, type EditViewButtonProps, EmptyState, type EmptyStateProps, ErrorAlert, type ErrorAlertProps, FilterDialog, FormBody, FormRoot, type FormRootProps, FormTitle, type GetColumnsConfigs, type GetDateColorProps, type GetMultiDatesProps, type GetRangeDatesProps, type GetStyleProps, type GetVariantProps, GlobalFilter, PageSizeControl, type PageSizeControlProps, Pagination, type RangeCalendarProps, type RangeDatePickerProps, RecordDisplay, type RecordDisplayProps, ReloadButton, type ReloadButtonProps, ResetFilteringButton, ResetSelectionButton, ResetSortingButton, type Result, RowCountText,
|
|
736
|
+
export { type CalendarProps, CardHeader, type CardHeaderProps, type CustomJSONSchema7Definition, DataDisplay, type DataDisplayProps, type DataResponse, DataTable, type DataTableDefaultState, type DataTableProps, DataTableServer, type DataTableServerProps, type DatePickerLabels, type DatePickerProps, DefaultCardTitle, DefaultForm, type DefaultFormProps, DefaultTable, type DefaultTableProps, DensityToggleButton, type DensityToggleButtonProps, type EditFilterButtonProps, EditSortingButton, type EditSortingButtonProps, type EditViewButtonProps, EmptyState, type EmptyStateProps, ErrorAlert, type ErrorAlertProps, FilterDialog, FormBody, FormRoot, type FormRootProps, FormTitle, type GetColumnsConfigs, type GetDateColorProps, type GetMultiDatesProps, type GetRangeDatesProps, type GetStyleProps, type GetVariantProps, GlobalFilter, PageSizeControl, type PageSizeControlProps, Pagination, type RangeCalendarProps, type RangeDatePickerProps, RecordDisplay, type RecordDisplayProps, ReloadButton, type ReloadButtonProps, ResetFilteringButton, ResetSelectionButton, ResetSortingButton, type Result, RowCountText, Table, TableBody, type TableBodyProps, TableCardContainer, type TableCardContainerProps, TableCards, type TableCardsProps, TableComponent, TableControls, type TableControlsProps, TableDataDisplay, type TableDataDisplayProps, TableFilter, TableFilterTags, TableFooter, type TableFooterProps, TableHeader, type TableHeaderProps, type TableHeaderTexts, TableLoadingComponent, type TableLoadingComponentProps, type TableProps, type TableRendererProps, type TableRowSelectorProps, TableSelector, TableSorter, TableViewer, TextCell, type TextCellProps, type UseDataTableProps, type UseDataTableReturn, type UseDataTableServerProps, type UseDataTableServerReturn, type UseFormProps, ViewDialog, getColumns, getMultiDates, getRangeDates, idPickerSanityCheck, useDataTable, useDataTableContext, useDataTableServer, useForm, widthSanityCheck };
|
package/dist/index.js
CHANGED
|
@@ -29,14 +29,13 @@ var gr = require('react-icons/gr');
|
|
|
29
29
|
var reactI18next = require('react-i18next');
|
|
30
30
|
var axios = require('axios');
|
|
31
31
|
var reactHookForm = require('react-hook-form');
|
|
32
|
-
var Ajv = require('ajv');
|
|
33
|
-
var addFormats = require('ajv-formats');
|
|
34
|
-
var zh_TW = require('ajv-i18n/localize/zh-TW');
|
|
35
|
-
var zh_CN = require('ajv-i18n/localize/zh');
|
|
36
32
|
var dayjs = require('dayjs');
|
|
37
33
|
var utc = require('dayjs/plugin/utc');
|
|
38
34
|
var timezone = require('dayjs/plugin/timezone');
|
|
39
35
|
var ti = require('react-icons/ti');
|
|
36
|
+
var Ajv = require('ajv');
|
|
37
|
+
var addFormats = require('ajv-formats');
|
|
38
|
+
var addErrors = require('ajv-errors');
|
|
40
39
|
|
|
41
40
|
function _interopNamespaceDefault(e) {
|
|
42
41
|
var n = Object.create(null);
|
|
@@ -3693,7 +3692,6 @@ const SchemaFormContext = React.createContext({
|
|
|
3693
3692
|
onSubmit: async () => { },
|
|
3694
3693
|
rowNumber: 0,
|
|
3695
3694
|
requestOptions: {},
|
|
3696
|
-
validationLocale: 'en',
|
|
3697
3695
|
timezone: 'Asia/Hong_Kong',
|
|
3698
3696
|
});
|
|
3699
3697
|
|
|
@@ -3705,179 +3703,6 @@ const clearEmptyString = (object) => {
|
|
|
3705
3703
|
return Object.fromEntries(Object.entries(object).filter(([, value]) => value !== ""));
|
|
3706
3704
|
};
|
|
3707
3705
|
|
|
3708
|
-
// AJV i18n support
|
|
3709
|
-
const localize = {
|
|
3710
|
-
en: () => { }, // English is default, no localization needed
|
|
3711
|
-
'zh-HK': zh_TW, // Use zh-TW for Hong Kong Traditional Chinese
|
|
3712
|
-
'zh-TW': zh_TW, // Traditional Chinese (Taiwan)
|
|
3713
|
-
'zh-CN': zh_CN, // Simplified Chinese
|
|
3714
|
-
'zh': zh_CN, // Simplified Chinese (short form)
|
|
3715
|
-
};
|
|
3716
|
-
// Create AJV instance with format support
|
|
3717
|
-
const createValidator = () => {
|
|
3718
|
-
const ajv = new Ajv({
|
|
3719
|
-
allErrors: true,
|
|
3720
|
-
verbose: true,
|
|
3721
|
-
removeAdditional: false,
|
|
3722
|
-
strict: false,
|
|
3723
|
-
messages: false, // Disable default messages for i18n
|
|
3724
|
-
});
|
|
3725
|
-
// Add format validation support (date, time, email, etc.)
|
|
3726
|
-
addFormats(ajv);
|
|
3727
|
-
return ajv;
|
|
3728
|
-
};
|
|
3729
|
-
/**
|
|
3730
|
-
* Validates data against a JSON Schema using AJV with i18n support
|
|
3731
|
-
* @param data - The data to validate
|
|
3732
|
-
* @param schema - The JSON Schema to validate against
|
|
3733
|
-
* @param options - Validation options including locale
|
|
3734
|
-
* @returns ValidationResult containing validation status and errors
|
|
3735
|
-
*/
|
|
3736
|
-
const validateData = (data, schema, options = {}) => {
|
|
3737
|
-
const { locale = 'en' } = options;
|
|
3738
|
-
const ajv = createValidator();
|
|
3739
|
-
try {
|
|
3740
|
-
const validate = ajv.compile(schema);
|
|
3741
|
-
const isValid = validate(data);
|
|
3742
|
-
if (isValid) {
|
|
3743
|
-
return {
|
|
3744
|
-
isValid: true,
|
|
3745
|
-
errors: [],
|
|
3746
|
-
};
|
|
3747
|
-
}
|
|
3748
|
-
// Apply localization if not English
|
|
3749
|
-
if (locale !== 'en' && validate.errors && localize[locale]) {
|
|
3750
|
-
try {
|
|
3751
|
-
localize[locale](validate.errors);
|
|
3752
|
-
}
|
|
3753
|
-
catch (error) {
|
|
3754
|
-
console.warn(`Failed to localize validation errors to ${locale}:`, error);
|
|
3755
|
-
}
|
|
3756
|
-
}
|
|
3757
|
-
const errors = (validate.errors || []).map((error) => {
|
|
3758
|
-
const field = error.instancePath?.replace(/^\//, '') || error.schemaPath?.split('/').pop() || 'root';
|
|
3759
|
-
let message = error.message || 'Validation error';
|
|
3760
|
-
// Enhanced error messages for better UX (only if using English or localization failed)
|
|
3761
|
-
if (locale === 'en' || !error.message) {
|
|
3762
|
-
switch (error.keyword) {
|
|
3763
|
-
case 'required':
|
|
3764
|
-
message = `${error.params?.missingProperty || 'Field'} is required`;
|
|
3765
|
-
break;
|
|
3766
|
-
case 'format':
|
|
3767
|
-
message = `Invalid ${error.params?.format} format`;
|
|
3768
|
-
break;
|
|
3769
|
-
case 'type':
|
|
3770
|
-
message = `Expected ${error.params?.type}, got ${typeof error.data}`;
|
|
3771
|
-
break;
|
|
3772
|
-
case 'minLength':
|
|
3773
|
-
message = `Must be at least ${error.params?.limit} characters`;
|
|
3774
|
-
break;
|
|
3775
|
-
case 'maxLength':
|
|
3776
|
-
message = `Must be no more than ${error.params?.limit} characters`;
|
|
3777
|
-
break;
|
|
3778
|
-
case 'minimum':
|
|
3779
|
-
message = `Must be at least ${error.params?.limit}`;
|
|
3780
|
-
break;
|
|
3781
|
-
case 'maximum':
|
|
3782
|
-
message = `Must be no more than ${error.params?.limit}`;
|
|
3783
|
-
break;
|
|
3784
|
-
case 'pattern':
|
|
3785
|
-
message = `Does not match required pattern`;
|
|
3786
|
-
break;
|
|
3787
|
-
case 'enum':
|
|
3788
|
-
message = `Must be one of: ${error.params?.allowedValues?.join(', ')}`;
|
|
3789
|
-
break;
|
|
3790
|
-
default:
|
|
3791
|
-
message = error.message || 'Validation error';
|
|
3792
|
-
}
|
|
3793
|
-
}
|
|
3794
|
-
return {
|
|
3795
|
-
field: field || error.instancePath || 'unknown',
|
|
3796
|
-
message,
|
|
3797
|
-
value: error.data,
|
|
3798
|
-
schemaPath: error.schemaPath,
|
|
3799
|
-
};
|
|
3800
|
-
});
|
|
3801
|
-
return {
|
|
3802
|
-
isValid: false,
|
|
3803
|
-
errors,
|
|
3804
|
-
};
|
|
3805
|
-
}
|
|
3806
|
-
catch (error) {
|
|
3807
|
-
// Handle AJV compilation errors
|
|
3808
|
-
const errorMessage = locale === 'zh-HK' || locale === 'zh-TW'
|
|
3809
|
-
? `架構驗證錯誤: ${error instanceof Error ? error.message : '未知錯誤'}`
|
|
3810
|
-
: locale === 'zh-CN' || locale === 'zh'
|
|
3811
|
-
? `模式验证错误: ${error instanceof Error ? error.message : '未知错误'}`
|
|
3812
|
-
: `Schema validation error: ${error instanceof Error ? error.message : 'Unknown error'}`;
|
|
3813
|
-
return {
|
|
3814
|
-
isValid: false,
|
|
3815
|
-
errors: [
|
|
3816
|
-
{
|
|
3817
|
-
field: 'schema',
|
|
3818
|
-
message: errorMessage,
|
|
3819
|
-
},
|
|
3820
|
-
],
|
|
3821
|
-
};
|
|
3822
|
-
}
|
|
3823
|
-
};
|
|
3824
|
-
/**
|
|
3825
|
-
* Creates a reusable validator function for a specific schema with i18n support
|
|
3826
|
-
* @param schema - The JSON Schema to create validator for
|
|
3827
|
-
* @param locale - The locale to use for error messages
|
|
3828
|
-
* @returns A function that validates data against the schema
|
|
3829
|
-
*/
|
|
3830
|
-
const createSchemaValidator = (schema, locale = 'en') => {
|
|
3831
|
-
const ajv = createValidator();
|
|
3832
|
-
const validate = ajv.compile(schema);
|
|
3833
|
-
return (data) => {
|
|
3834
|
-
const isValid = validate(data);
|
|
3835
|
-
if (isValid) {
|
|
3836
|
-
return {
|
|
3837
|
-
isValid: true,
|
|
3838
|
-
errors: [],
|
|
3839
|
-
};
|
|
3840
|
-
}
|
|
3841
|
-
// Apply localization if not English
|
|
3842
|
-
if (locale !== 'en' && validate.errors && localize[locale]) {
|
|
3843
|
-
try {
|
|
3844
|
-
localize[locale](validate.errors);
|
|
3845
|
-
}
|
|
3846
|
-
catch (error) {
|
|
3847
|
-
console.warn(`Failed to localize validation errors to ${locale}:`, error);
|
|
3848
|
-
}
|
|
3849
|
-
}
|
|
3850
|
-
const errors = (validate.errors || []).map((error) => {
|
|
3851
|
-
const field = error.instancePath?.replace(/^\//, '') || 'root';
|
|
3852
|
-
return {
|
|
3853
|
-
field,
|
|
3854
|
-
message: error.message || 'Validation error',
|
|
3855
|
-
value: error.data,
|
|
3856
|
-
schemaPath: error.schemaPath,
|
|
3857
|
-
};
|
|
3858
|
-
});
|
|
3859
|
-
return {
|
|
3860
|
-
isValid: false,
|
|
3861
|
-
errors,
|
|
3862
|
-
};
|
|
3863
|
-
};
|
|
3864
|
-
};
|
|
3865
|
-
/**
|
|
3866
|
-
* Get available locales for validation error messages
|
|
3867
|
-
* @returns Array of supported locale codes
|
|
3868
|
-
*/
|
|
3869
|
-
const getSupportedLocales = () => {
|
|
3870
|
-
return Object.keys(localize);
|
|
3871
|
-
};
|
|
3872
|
-
/**
|
|
3873
|
-
* Check if a locale is supported
|
|
3874
|
-
* @param locale - The locale to check
|
|
3875
|
-
* @returns Boolean indicating if the locale is supported
|
|
3876
|
-
*/
|
|
3877
|
-
const isLocaleSupported = (locale) => {
|
|
3878
|
-
return locale in localize;
|
|
3879
|
-
};
|
|
3880
|
-
|
|
3881
3706
|
const idPickerSanityCheck = (column, foreign_key) => {
|
|
3882
3707
|
if (!!foreign_key == false) {
|
|
3883
3708
|
throw new Error(`The key foreign_key does not exist in properties of column ${column} when using id-picker.`);
|
|
@@ -3893,7 +3718,7 @@ const idPickerSanityCheck = (column, foreign_key) => {
|
|
|
3893
3718
|
throw new Error(`The key column does not exist in properties of column ${column} when using id-picker.`);
|
|
3894
3719
|
}
|
|
3895
3720
|
};
|
|
3896
|
-
const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, children, order = [], ignore = [], include = [], onSubmit = undefined, rowNumber = undefined, requestOptions = {}, getUpdatedData = () => { }, customErrorRenderer,
|
|
3721
|
+
const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, children, order = [], ignore = [], include = [], onSubmit = undefined, rowNumber = undefined, requestOptions = {}, getUpdatedData = () => { }, customErrorRenderer, }) => {
|
|
3897
3722
|
const [isSuccess, setIsSuccess] = React.useState(false);
|
|
3898
3723
|
const [isError, setIsError] = React.useState(false);
|
|
3899
3724
|
const [isSubmiting, setIsSubmiting] = React.useState(false);
|
|
@@ -3927,7 +3752,6 @@ const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, childre
|
|
|
3927
3752
|
setError,
|
|
3928
3753
|
getUpdatedData,
|
|
3929
3754
|
customErrorRenderer,
|
|
3930
|
-
validationLocale,
|
|
3931
3755
|
}, children: jsxRuntime.jsx(reactHookForm.FormProvider, { ...form, children: children }) }));
|
|
3932
3756
|
};
|
|
3933
3757
|
|
|
@@ -6008,24 +5832,20 @@ const ColumnViewer = ({ column, properties, prefix, }) => {
|
|
|
6008
5832
|
};
|
|
6009
5833
|
|
|
6010
5834
|
const SubmitButton = () => {
|
|
6011
|
-
const { translate, setValidatedData, setIsError, setIsConfirming, setError, schema,
|
|
5835
|
+
const { translate, setValidatedData, setIsError, setIsConfirming, setError, schema, } = useSchemaContext();
|
|
6012
5836
|
const methods = reactHookForm.useFormContext();
|
|
6013
5837
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6014
5838
|
const onValid = (data) => {
|
|
6015
5839
|
// Validate data using AJV before proceeding to confirmation
|
|
6016
|
-
const
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
? '表单验证失败'
|
|
6026
|
-
: 'Form validation failed'
|
|
6027
|
-
};
|
|
6028
|
-
setError(validationErrorMessage);
|
|
5840
|
+
const validate = new Ajv({
|
|
5841
|
+
strict: false,
|
|
5842
|
+
allErrors: true,
|
|
5843
|
+
}).compile(schema);
|
|
5844
|
+
const validationResult = validate(data);
|
|
5845
|
+
// @ts-expect-error TODO: find appropriate type
|
|
5846
|
+
const errors = validationResult.errors;
|
|
5847
|
+
if (errors && errors.length > 0) {
|
|
5848
|
+
setError(errors);
|
|
6029
5849
|
setIsError(true);
|
|
6030
5850
|
return;
|
|
6031
5851
|
}
|
|
@@ -6040,7 +5860,7 @@ const SubmitButton = () => {
|
|
|
6040
5860
|
};
|
|
6041
5861
|
|
|
6042
5862
|
const FormBody = () => {
|
|
6043
|
-
const { schema, requestUrl, order, ignore, include, onSubmit, rowNumber, translate, requestOptions, isSuccess, setIsSuccess, isError, setIsError, isSubmiting, setIsSubmiting, isConfirming, setIsConfirming, validatedData, setValidatedData, error, setError, getUpdatedData, customErrorRenderer,
|
|
5863
|
+
const { schema, requestUrl, order, ignore, include, onSubmit, rowNumber, translate, requestOptions, isSuccess, setIsSuccess, isError, setIsError, isSubmiting, setIsSubmiting, isConfirming, setIsConfirming, validatedData, setValidatedData, error, setError, getUpdatedData, customErrorRenderer, } = useSchemaContext();
|
|
6044
5864
|
const methods = reactHookForm.useFormContext();
|
|
6045
5865
|
const { properties } = schema;
|
|
6046
5866
|
const onBeforeSubmit = () => {
|
|
@@ -6059,23 +5879,31 @@ const FormBody = () => {
|
|
|
6059
5879
|
// Enhanced validation function using AJV with i18n support
|
|
6060
5880
|
const validateFormData = (data) => {
|
|
6061
5881
|
try {
|
|
6062
|
-
const
|
|
6063
|
-
|
|
5882
|
+
const ajv = new Ajv({
|
|
5883
|
+
strict: false,
|
|
5884
|
+
allErrors: true,
|
|
6064
5885
|
});
|
|
6065
|
-
|
|
5886
|
+
addFormats(ajv);
|
|
5887
|
+
addErrors(ajv);
|
|
5888
|
+
const validate = ajv.compile(schema);
|
|
5889
|
+
const validationResult = validate(data);
|
|
5890
|
+
const errors = validate.errors;
|
|
5891
|
+
console.log({
|
|
5892
|
+
isValid: validationResult,
|
|
5893
|
+
errors,
|
|
5894
|
+
}, "plkdfs");
|
|
5895
|
+
return {
|
|
5896
|
+
isValid: validationResult,
|
|
5897
|
+
errors,
|
|
5898
|
+
};
|
|
6066
5899
|
}
|
|
6067
5900
|
catch (error) {
|
|
6068
|
-
const errorMessage = validationLocale === "zh-HK" || validationLocale === "zh-TW"
|
|
6069
|
-
? `驗證錯誤: ${error instanceof Error ? error.message : "未知驗證錯誤"}`
|
|
6070
|
-
: validationLocale === "zh-CN" || validationLocale === "zh"
|
|
6071
|
-
? `验证错误: ${error instanceof Error ? error.message : "未知验证错误"}`
|
|
6072
|
-
: `Validation error: ${error instanceof Error ? error.message : "Unknown validation error"}`;
|
|
6073
5901
|
return {
|
|
6074
5902
|
isValid: false,
|
|
6075
5903
|
errors: [
|
|
6076
5904
|
{
|
|
6077
5905
|
field: "validation",
|
|
6078
|
-
message:
|
|
5906
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
6079
5907
|
},
|
|
6080
5908
|
],
|
|
6081
5909
|
};
|
|
@@ -6112,11 +5940,7 @@ const FormBody = () => {
|
|
|
6112
5940
|
const validationErrorMessage = {
|
|
6113
5941
|
type: "validation",
|
|
6114
5942
|
errors: validationResult.errors,
|
|
6115
|
-
message:
|
|
6116
|
-
? "表單驗證失敗"
|
|
6117
|
-
: validationLocale === "zh-CN" || validationLocale === "zh"
|
|
6118
|
-
? "表单验证失败"
|
|
6119
|
-
: "Form validation failed",
|
|
5943
|
+
message: translate.t("validation_error"),
|
|
6120
5944
|
};
|
|
6121
5945
|
onSubmitError(validationErrorMessage);
|
|
6122
5946
|
return;
|
|
@@ -6129,22 +5953,7 @@ const FormBody = () => {
|
|
|
6129
5953
|
};
|
|
6130
5954
|
// Custom error renderer for validation errors with i18n support
|
|
6131
5955
|
const renderValidationErrors = (validationErrors) => {
|
|
6132
|
-
|
|
6133
|
-
? `表單驗證失敗 (${validationErrors.length} 個錯誤${validationErrors.length > 1 ? "" : ""})`
|
|
6134
|
-
: validationLocale === "zh-CN" || validationLocale === "zh"
|
|
6135
|
-
? `表单验证失败 (${validationErrors.length} 个错误${validationErrors.length > 1 ? "" : ""})`
|
|
6136
|
-
: `Form Validation Failed (${validationErrors.length} error${validationErrors.length > 1 ? "s" : ""})`;
|
|
6137
|
-
const formLabel = validationLocale === "zh-HK" || validationLocale === "zh-TW"
|
|
6138
|
-
? "表單"
|
|
6139
|
-
: validationLocale === "zh-CN" || validationLocale === "zh"
|
|
6140
|
-
? "表单"
|
|
6141
|
-
: "Form";
|
|
6142
|
-
const currentValueLabel = validationLocale === "zh-HK" || validationLocale === "zh-TW"
|
|
6143
|
-
? "目前值:"
|
|
6144
|
-
: validationLocale === "zh-CN" || validationLocale === "zh"
|
|
6145
|
-
? "当前值:"
|
|
6146
|
-
: "Current value:";
|
|
6147
|
-
return (jsxRuntime.jsx(AccordionRoot, { collapsible: true, defaultValue: [], children: jsxRuntime.jsxs(AccordionItem, { value: "validation-errors", children: [jsxRuntime.jsx(AccordionItemTrigger, { children: title }), jsxRuntime.jsx(AccordionItemContent, { children: validationErrors.map((err, index) => (jsxRuntime.jsxs(react.Box, { mb: 2, p: 2, bg: "red.50", borderLeft: "4px solid", borderColor: "red.500", children: [jsxRuntime.jsxs(react.Text, { fontWeight: "bold", color: "red.700", children: [err.field === "root" ? formLabel : err.field, ":"] }), jsxRuntime.jsx(react.Text, { color: "red.600", children: err.message }), err.value !== undefined && (jsxRuntime.jsxs(react.Text, { fontSize: "sm", color: "red.500", mt: 1, whiteSpace: "pre-wrap", wordBreak: "break-all", children: [currentValueLabel, " ", JSON.stringify(err.value, null, 2)] }))] }, index))) })] }) }));
|
|
5956
|
+
return (jsxRuntime.jsx(AccordionRoot, { colorPalette: "red", collapsible: true, defaultValue: [], children: jsxRuntime.jsxs(AccordionItem, { value: "validation-errors", children: [jsxRuntime.jsx(AccordionItemTrigger, { children: translate.t("validation_error") }), jsxRuntime.jsx(AccordionItemContent, { display: "flex", flexFlow: "column", gap: "2", children: validationErrors.map((err, index) => (jsxRuntime.jsxs(react.AlertRoot, { status: "error", children: [jsxRuntime.jsx(react.AlertIndicator, {}), jsxRuntime.jsxs(react.AlertContent, { children: [jsxRuntime.jsx(react.AlertTitle, { fontWeight: "bold", children: err.instancePath }), jsxRuntime.jsx(react.AlertDescription, { children: err.message }), err.params !== undefined && (jsxRuntime.jsx(react.AlertDescription, { whiteSpace: "pre-wrap", wordBreak: "break-all", children: JSON.stringify(err.data, null, 2) }))] })] }))) })] }) }));
|
|
6148
5957
|
};
|
|
6149
5958
|
const renderColumns = ({ order, keys, ignore, include, }) => {
|
|
6150
5959
|
const included = include.length > 0 ? include : keys;
|
|
@@ -6276,16 +6085,12 @@ exports.TableSorter = TableSorter;
|
|
|
6276
6085
|
exports.TableViewer = TableViewer;
|
|
6277
6086
|
exports.TextCell = TextCell;
|
|
6278
6087
|
exports.ViewDialog = ViewDialog;
|
|
6279
|
-
exports.createSchemaValidator = createSchemaValidator;
|
|
6280
6088
|
exports.getColumns = getColumns;
|
|
6281
6089
|
exports.getMultiDates = getMultiDates;
|
|
6282
6090
|
exports.getRangeDates = getRangeDates;
|
|
6283
|
-
exports.getSupportedLocales = getSupportedLocales;
|
|
6284
6091
|
exports.idPickerSanityCheck = idPickerSanityCheck;
|
|
6285
|
-
exports.isLocaleSupported = isLocaleSupported;
|
|
6286
6092
|
exports.useDataTable = useDataTable;
|
|
6287
6093
|
exports.useDataTableContext = useDataTableContext;
|
|
6288
6094
|
exports.useDataTableServer = useDataTableServer;
|
|
6289
6095
|
exports.useForm = useForm;
|
|
6290
|
-
exports.validateData = validateData;
|
|
6291
6096
|
exports.widthSanityCheck = widthSanityCheck;
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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, Accordion, Field as Field$1, Popover, NumberInput, Show, RadioCard, CheckboxGroup, Textarea, Center, Heading } from '@chakra-ui/react';
|
|
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, Accordion, Field as Field$1, Popover, NumberInput, Show, RadioCard, CheckboxGroup, Textarea, Center, AlertRoot, AlertIndicator, AlertContent, AlertTitle, AlertDescription, 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 } from 'react';
|
|
@@ -28,14 +28,13 @@ import { GrAscend, GrDescend } from 'react-icons/gr';
|
|
|
28
28
|
import { useTranslation } from 'react-i18next';
|
|
29
29
|
import axios from 'axios';
|
|
30
30
|
import { FormProvider, useFormContext, useForm as useForm$1 } from 'react-hook-form';
|
|
31
|
-
import Ajv from 'ajv';
|
|
32
|
-
import addFormats from 'ajv-formats';
|
|
33
|
-
import zh_TW from 'ajv-i18n/localize/zh-TW';
|
|
34
|
-
import zh_CN from 'ajv-i18n/localize/zh';
|
|
35
31
|
import dayjs from 'dayjs';
|
|
36
32
|
import utc from 'dayjs/plugin/utc';
|
|
37
33
|
import timezone from 'dayjs/plugin/timezone';
|
|
38
34
|
import { TiDeleteOutline } from 'react-icons/ti';
|
|
35
|
+
import Ajv from 'ajv';
|
|
36
|
+
import addFormats from 'ajv-formats';
|
|
37
|
+
import addErrors from 'ajv-errors';
|
|
39
38
|
|
|
40
39
|
const DataTableContext = createContext({
|
|
41
40
|
table: {},
|
|
@@ -3673,7 +3672,6 @@ const SchemaFormContext = createContext({
|
|
|
3673
3672
|
onSubmit: async () => { },
|
|
3674
3673
|
rowNumber: 0,
|
|
3675
3674
|
requestOptions: {},
|
|
3676
|
-
validationLocale: 'en',
|
|
3677
3675
|
timezone: 'Asia/Hong_Kong',
|
|
3678
3676
|
});
|
|
3679
3677
|
|
|
@@ -3685,179 +3683,6 @@ const clearEmptyString = (object) => {
|
|
|
3685
3683
|
return Object.fromEntries(Object.entries(object).filter(([, value]) => value !== ""));
|
|
3686
3684
|
};
|
|
3687
3685
|
|
|
3688
|
-
// AJV i18n support
|
|
3689
|
-
const localize = {
|
|
3690
|
-
en: () => { }, // English is default, no localization needed
|
|
3691
|
-
'zh-HK': zh_TW, // Use zh-TW for Hong Kong Traditional Chinese
|
|
3692
|
-
'zh-TW': zh_TW, // Traditional Chinese (Taiwan)
|
|
3693
|
-
'zh-CN': zh_CN, // Simplified Chinese
|
|
3694
|
-
'zh': zh_CN, // Simplified Chinese (short form)
|
|
3695
|
-
};
|
|
3696
|
-
// Create AJV instance with format support
|
|
3697
|
-
const createValidator = () => {
|
|
3698
|
-
const ajv = new Ajv({
|
|
3699
|
-
allErrors: true,
|
|
3700
|
-
verbose: true,
|
|
3701
|
-
removeAdditional: false,
|
|
3702
|
-
strict: false,
|
|
3703
|
-
messages: false, // Disable default messages for i18n
|
|
3704
|
-
});
|
|
3705
|
-
// Add format validation support (date, time, email, etc.)
|
|
3706
|
-
addFormats(ajv);
|
|
3707
|
-
return ajv;
|
|
3708
|
-
};
|
|
3709
|
-
/**
|
|
3710
|
-
* Validates data against a JSON Schema using AJV with i18n support
|
|
3711
|
-
* @param data - The data to validate
|
|
3712
|
-
* @param schema - The JSON Schema to validate against
|
|
3713
|
-
* @param options - Validation options including locale
|
|
3714
|
-
* @returns ValidationResult containing validation status and errors
|
|
3715
|
-
*/
|
|
3716
|
-
const validateData = (data, schema, options = {}) => {
|
|
3717
|
-
const { locale = 'en' } = options;
|
|
3718
|
-
const ajv = createValidator();
|
|
3719
|
-
try {
|
|
3720
|
-
const validate = ajv.compile(schema);
|
|
3721
|
-
const isValid = validate(data);
|
|
3722
|
-
if (isValid) {
|
|
3723
|
-
return {
|
|
3724
|
-
isValid: true,
|
|
3725
|
-
errors: [],
|
|
3726
|
-
};
|
|
3727
|
-
}
|
|
3728
|
-
// Apply localization if not English
|
|
3729
|
-
if (locale !== 'en' && validate.errors && localize[locale]) {
|
|
3730
|
-
try {
|
|
3731
|
-
localize[locale](validate.errors);
|
|
3732
|
-
}
|
|
3733
|
-
catch (error) {
|
|
3734
|
-
console.warn(`Failed to localize validation errors to ${locale}:`, error);
|
|
3735
|
-
}
|
|
3736
|
-
}
|
|
3737
|
-
const errors = (validate.errors || []).map((error) => {
|
|
3738
|
-
const field = error.instancePath?.replace(/^\//, '') || error.schemaPath?.split('/').pop() || 'root';
|
|
3739
|
-
let message = error.message || 'Validation error';
|
|
3740
|
-
// Enhanced error messages for better UX (only if using English or localization failed)
|
|
3741
|
-
if (locale === 'en' || !error.message) {
|
|
3742
|
-
switch (error.keyword) {
|
|
3743
|
-
case 'required':
|
|
3744
|
-
message = `${error.params?.missingProperty || 'Field'} is required`;
|
|
3745
|
-
break;
|
|
3746
|
-
case 'format':
|
|
3747
|
-
message = `Invalid ${error.params?.format} format`;
|
|
3748
|
-
break;
|
|
3749
|
-
case 'type':
|
|
3750
|
-
message = `Expected ${error.params?.type}, got ${typeof error.data}`;
|
|
3751
|
-
break;
|
|
3752
|
-
case 'minLength':
|
|
3753
|
-
message = `Must be at least ${error.params?.limit} characters`;
|
|
3754
|
-
break;
|
|
3755
|
-
case 'maxLength':
|
|
3756
|
-
message = `Must be no more than ${error.params?.limit} characters`;
|
|
3757
|
-
break;
|
|
3758
|
-
case 'minimum':
|
|
3759
|
-
message = `Must be at least ${error.params?.limit}`;
|
|
3760
|
-
break;
|
|
3761
|
-
case 'maximum':
|
|
3762
|
-
message = `Must be no more than ${error.params?.limit}`;
|
|
3763
|
-
break;
|
|
3764
|
-
case 'pattern':
|
|
3765
|
-
message = `Does not match required pattern`;
|
|
3766
|
-
break;
|
|
3767
|
-
case 'enum':
|
|
3768
|
-
message = `Must be one of: ${error.params?.allowedValues?.join(', ')}`;
|
|
3769
|
-
break;
|
|
3770
|
-
default:
|
|
3771
|
-
message = error.message || 'Validation error';
|
|
3772
|
-
}
|
|
3773
|
-
}
|
|
3774
|
-
return {
|
|
3775
|
-
field: field || error.instancePath || 'unknown',
|
|
3776
|
-
message,
|
|
3777
|
-
value: error.data,
|
|
3778
|
-
schemaPath: error.schemaPath,
|
|
3779
|
-
};
|
|
3780
|
-
});
|
|
3781
|
-
return {
|
|
3782
|
-
isValid: false,
|
|
3783
|
-
errors,
|
|
3784
|
-
};
|
|
3785
|
-
}
|
|
3786
|
-
catch (error) {
|
|
3787
|
-
// Handle AJV compilation errors
|
|
3788
|
-
const errorMessage = locale === 'zh-HK' || locale === 'zh-TW'
|
|
3789
|
-
? `架構驗證錯誤: ${error instanceof Error ? error.message : '未知錯誤'}`
|
|
3790
|
-
: locale === 'zh-CN' || locale === 'zh'
|
|
3791
|
-
? `模式验证错误: ${error instanceof Error ? error.message : '未知错误'}`
|
|
3792
|
-
: `Schema validation error: ${error instanceof Error ? error.message : 'Unknown error'}`;
|
|
3793
|
-
return {
|
|
3794
|
-
isValid: false,
|
|
3795
|
-
errors: [
|
|
3796
|
-
{
|
|
3797
|
-
field: 'schema',
|
|
3798
|
-
message: errorMessage,
|
|
3799
|
-
},
|
|
3800
|
-
],
|
|
3801
|
-
};
|
|
3802
|
-
}
|
|
3803
|
-
};
|
|
3804
|
-
/**
|
|
3805
|
-
* Creates a reusable validator function for a specific schema with i18n support
|
|
3806
|
-
* @param schema - The JSON Schema to create validator for
|
|
3807
|
-
* @param locale - The locale to use for error messages
|
|
3808
|
-
* @returns A function that validates data against the schema
|
|
3809
|
-
*/
|
|
3810
|
-
const createSchemaValidator = (schema, locale = 'en') => {
|
|
3811
|
-
const ajv = createValidator();
|
|
3812
|
-
const validate = ajv.compile(schema);
|
|
3813
|
-
return (data) => {
|
|
3814
|
-
const isValid = validate(data);
|
|
3815
|
-
if (isValid) {
|
|
3816
|
-
return {
|
|
3817
|
-
isValid: true,
|
|
3818
|
-
errors: [],
|
|
3819
|
-
};
|
|
3820
|
-
}
|
|
3821
|
-
// Apply localization if not English
|
|
3822
|
-
if (locale !== 'en' && validate.errors && localize[locale]) {
|
|
3823
|
-
try {
|
|
3824
|
-
localize[locale](validate.errors);
|
|
3825
|
-
}
|
|
3826
|
-
catch (error) {
|
|
3827
|
-
console.warn(`Failed to localize validation errors to ${locale}:`, error);
|
|
3828
|
-
}
|
|
3829
|
-
}
|
|
3830
|
-
const errors = (validate.errors || []).map((error) => {
|
|
3831
|
-
const field = error.instancePath?.replace(/^\//, '') || 'root';
|
|
3832
|
-
return {
|
|
3833
|
-
field,
|
|
3834
|
-
message: error.message || 'Validation error',
|
|
3835
|
-
value: error.data,
|
|
3836
|
-
schemaPath: error.schemaPath,
|
|
3837
|
-
};
|
|
3838
|
-
});
|
|
3839
|
-
return {
|
|
3840
|
-
isValid: false,
|
|
3841
|
-
errors,
|
|
3842
|
-
};
|
|
3843
|
-
};
|
|
3844
|
-
};
|
|
3845
|
-
/**
|
|
3846
|
-
* Get available locales for validation error messages
|
|
3847
|
-
* @returns Array of supported locale codes
|
|
3848
|
-
*/
|
|
3849
|
-
const getSupportedLocales = () => {
|
|
3850
|
-
return Object.keys(localize);
|
|
3851
|
-
};
|
|
3852
|
-
/**
|
|
3853
|
-
* Check if a locale is supported
|
|
3854
|
-
* @param locale - The locale to check
|
|
3855
|
-
* @returns Boolean indicating if the locale is supported
|
|
3856
|
-
*/
|
|
3857
|
-
const isLocaleSupported = (locale) => {
|
|
3858
|
-
return locale in localize;
|
|
3859
|
-
};
|
|
3860
|
-
|
|
3861
3686
|
const idPickerSanityCheck = (column, foreign_key) => {
|
|
3862
3687
|
if (!!foreign_key == false) {
|
|
3863
3688
|
throw new Error(`The key foreign_key does not exist in properties of column ${column} when using id-picker.`);
|
|
@@ -3873,7 +3698,7 @@ const idPickerSanityCheck = (column, foreign_key) => {
|
|
|
3873
3698
|
throw new Error(`The key column does not exist in properties of column ${column} when using id-picker.`);
|
|
3874
3699
|
}
|
|
3875
3700
|
};
|
|
3876
|
-
const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, children, order = [], ignore = [], include = [], onSubmit = undefined, rowNumber = undefined, requestOptions = {}, getUpdatedData = () => { }, customErrorRenderer,
|
|
3701
|
+
const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, children, order = [], ignore = [], include = [], onSubmit = undefined, rowNumber = undefined, requestOptions = {}, getUpdatedData = () => { }, customErrorRenderer, }) => {
|
|
3877
3702
|
const [isSuccess, setIsSuccess] = useState(false);
|
|
3878
3703
|
const [isError, setIsError] = useState(false);
|
|
3879
3704
|
const [isSubmiting, setIsSubmiting] = useState(false);
|
|
@@ -3907,7 +3732,6 @@ const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, childre
|
|
|
3907
3732
|
setError,
|
|
3908
3733
|
getUpdatedData,
|
|
3909
3734
|
customErrorRenderer,
|
|
3910
|
-
validationLocale,
|
|
3911
3735
|
}, children: jsx(FormProvider, { ...form, children: children }) }));
|
|
3912
3736
|
};
|
|
3913
3737
|
|
|
@@ -5988,24 +5812,20 @@ const ColumnViewer = ({ column, properties, prefix, }) => {
|
|
|
5988
5812
|
};
|
|
5989
5813
|
|
|
5990
5814
|
const SubmitButton = () => {
|
|
5991
|
-
const { translate, setValidatedData, setIsError, setIsConfirming, setError, schema,
|
|
5815
|
+
const { translate, setValidatedData, setIsError, setIsConfirming, setError, schema, } = useSchemaContext();
|
|
5992
5816
|
const methods = useFormContext();
|
|
5993
5817
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5994
5818
|
const onValid = (data) => {
|
|
5995
5819
|
// Validate data using AJV before proceeding to confirmation
|
|
5996
|
-
const
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
? '表单验证失败'
|
|
6006
|
-
: 'Form validation failed'
|
|
6007
|
-
};
|
|
6008
|
-
setError(validationErrorMessage);
|
|
5820
|
+
const validate = new Ajv({
|
|
5821
|
+
strict: false,
|
|
5822
|
+
allErrors: true,
|
|
5823
|
+
}).compile(schema);
|
|
5824
|
+
const validationResult = validate(data);
|
|
5825
|
+
// @ts-expect-error TODO: find appropriate type
|
|
5826
|
+
const errors = validationResult.errors;
|
|
5827
|
+
if (errors && errors.length > 0) {
|
|
5828
|
+
setError(errors);
|
|
6009
5829
|
setIsError(true);
|
|
6010
5830
|
return;
|
|
6011
5831
|
}
|
|
@@ -6020,7 +5840,7 @@ const SubmitButton = () => {
|
|
|
6020
5840
|
};
|
|
6021
5841
|
|
|
6022
5842
|
const FormBody = () => {
|
|
6023
|
-
const { schema, requestUrl, order, ignore, include, onSubmit, rowNumber, translate, requestOptions, isSuccess, setIsSuccess, isError, setIsError, isSubmiting, setIsSubmiting, isConfirming, setIsConfirming, validatedData, setValidatedData, error, setError, getUpdatedData, customErrorRenderer,
|
|
5843
|
+
const { schema, requestUrl, order, ignore, include, onSubmit, rowNumber, translate, requestOptions, isSuccess, setIsSuccess, isError, setIsError, isSubmiting, setIsSubmiting, isConfirming, setIsConfirming, validatedData, setValidatedData, error, setError, getUpdatedData, customErrorRenderer, } = useSchemaContext();
|
|
6024
5844
|
const methods = useFormContext();
|
|
6025
5845
|
const { properties } = schema;
|
|
6026
5846
|
const onBeforeSubmit = () => {
|
|
@@ -6039,23 +5859,31 @@ const FormBody = () => {
|
|
|
6039
5859
|
// Enhanced validation function using AJV with i18n support
|
|
6040
5860
|
const validateFormData = (data) => {
|
|
6041
5861
|
try {
|
|
6042
|
-
const
|
|
6043
|
-
|
|
5862
|
+
const ajv = new Ajv({
|
|
5863
|
+
strict: false,
|
|
5864
|
+
allErrors: true,
|
|
6044
5865
|
});
|
|
6045
|
-
|
|
5866
|
+
addFormats(ajv);
|
|
5867
|
+
addErrors(ajv);
|
|
5868
|
+
const validate = ajv.compile(schema);
|
|
5869
|
+
const validationResult = validate(data);
|
|
5870
|
+
const errors = validate.errors;
|
|
5871
|
+
console.log({
|
|
5872
|
+
isValid: validationResult,
|
|
5873
|
+
errors,
|
|
5874
|
+
}, "plkdfs");
|
|
5875
|
+
return {
|
|
5876
|
+
isValid: validationResult,
|
|
5877
|
+
errors,
|
|
5878
|
+
};
|
|
6046
5879
|
}
|
|
6047
5880
|
catch (error) {
|
|
6048
|
-
const errorMessage = validationLocale === "zh-HK" || validationLocale === "zh-TW"
|
|
6049
|
-
? `驗證錯誤: ${error instanceof Error ? error.message : "未知驗證錯誤"}`
|
|
6050
|
-
: validationLocale === "zh-CN" || validationLocale === "zh"
|
|
6051
|
-
? `验证错误: ${error instanceof Error ? error.message : "未知验证错误"}`
|
|
6052
|
-
: `Validation error: ${error instanceof Error ? error.message : "Unknown validation error"}`;
|
|
6053
5881
|
return {
|
|
6054
5882
|
isValid: false,
|
|
6055
5883
|
errors: [
|
|
6056
5884
|
{
|
|
6057
5885
|
field: "validation",
|
|
6058
|
-
message:
|
|
5886
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
6059
5887
|
},
|
|
6060
5888
|
],
|
|
6061
5889
|
};
|
|
@@ -6092,11 +5920,7 @@ const FormBody = () => {
|
|
|
6092
5920
|
const validationErrorMessage = {
|
|
6093
5921
|
type: "validation",
|
|
6094
5922
|
errors: validationResult.errors,
|
|
6095
|
-
message:
|
|
6096
|
-
? "表單驗證失敗"
|
|
6097
|
-
: validationLocale === "zh-CN" || validationLocale === "zh"
|
|
6098
|
-
? "表单验证失败"
|
|
6099
|
-
: "Form validation failed",
|
|
5923
|
+
message: translate.t("validation_error"),
|
|
6100
5924
|
};
|
|
6101
5925
|
onSubmitError(validationErrorMessage);
|
|
6102
5926
|
return;
|
|
@@ -6109,22 +5933,7 @@ const FormBody = () => {
|
|
|
6109
5933
|
};
|
|
6110
5934
|
// Custom error renderer for validation errors with i18n support
|
|
6111
5935
|
const renderValidationErrors = (validationErrors) => {
|
|
6112
|
-
|
|
6113
|
-
? `表單驗證失敗 (${validationErrors.length} 個錯誤${validationErrors.length > 1 ? "" : ""})`
|
|
6114
|
-
: validationLocale === "zh-CN" || validationLocale === "zh"
|
|
6115
|
-
? `表单验证失败 (${validationErrors.length} 个错误${validationErrors.length > 1 ? "" : ""})`
|
|
6116
|
-
: `Form Validation Failed (${validationErrors.length} error${validationErrors.length > 1 ? "s" : ""})`;
|
|
6117
|
-
const formLabel = validationLocale === "zh-HK" || validationLocale === "zh-TW"
|
|
6118
|
-
? "表單"
|
|
6119
|
-
: validationLocale === "zh-CN" || validationLocale === "zh"
|
|
6120
|
-
? "表单"
|
|
6121
|
-
: "Form";
|
|
6122
|
-
const currentValueLabel = validationLocale === "zh-HK" || validationLocale === "zh-TW"
|
|
6123
|
-
? "目前值:"
|
|
6124
|
-
: validationLocale === "zh-CN" || validationLocale === "zh"
|
|
6125
|
-
? "当前值:"
|
|
6126
|
-
: "Current value:";
|
|
6127
|
-
return (jsx(AccordionRoot, { collapsible: true, defaultValue: [], children: jsxs(AccordionItem, { value: "validation-errors", children: [jsx(AccordionItemTrigger, { children: title }), jsx(AccordionItemContent, { children: validationErrors.map((err, index) => (jsxs(Box, { mb: 2, p: 2, bg: "red.50", borderLeft: "4px solid", borderColor: "red.500", children: [jsxs(Text, { fontWeight: "bold", color: "red.700", children: [err.field === "root" ? formLabel : err.field, ":"] }), jsx(Text, { color: "red.600", children: err.message }), err.value !== undefined && (jsxs(Text, { fontSize: "sm", color: "red.500", mt: 1, whiteSpace: "pre-wrap", wordBreak: "break-all", children: [currentValueLabel, " ", JSON.stringify(err.value, null, 2)] }))] }, index))) })] }) }));
|
|
5936
|
+
return (jsx(AccordionRoot, { colorPalette: "red", collapsible: true, defaultValue: [], children: jsxs(AccordionItem, { value: "validation-errors", children: [jsx(AccordionItemTrigger, { children: translate.t("validation_error") }), jsx(AccordionItemContent, { display: "flex", flexFlow: "column", gap: "2", children: validationErrors.map((err, index) => (jsxs(AlertRoot, { status: "error", children: [jsx(AlertIndicator, {}), jsxs(AlertContent, { children: [jsx(AlertTitle, { fontWeight: "bold", children: err.instancePath }), jsx(AlertDescription, { children: err.message }), err.params !== undefined && (jsx(AlertDescription, { whiteSpace: "pre-wrap", wordBreak: "break-all", children: JSON.stringify(err.data, null, 2) }))] })] }))) })] }) }));
|
|
6128
5937
|
};
|
|
6129
5938
|
const renderColumns = ({ order, keys, ignore, include, }) => {
|
|
6130
5939
|
const included = include.length > 0 ? include : keys;
|
|
@@ -6215,4 +6024,4 @@ const getMultiDates = ({ selected, selectedDate, selectedDates, selectable, }) =
|
|
|
6215
6024
|
}
|
|
6216
6025
|
};
|
|
6217
6026
|
|
|
6218
|
-
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,
|
|
6027
|
+
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, getColumns, getMultiDates, getRangeDates, idPickerSanityCheck, useDataTable, useDataTableContext, useDataTableServer, useForm, widthSanityCheck };
|
|
@@ -3,7 +3,6 @@ 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 { SupportedLocale } from "./utils/validation";
|
|
7
6
|
export interface SchemaFormContext<TData extends FieldValues> {
|
|
8
7
|
schema: JSONSchema7;
|
|
9
8
|
serverUrl: string;
|
|
@@ -31,7 +30,6 @@ export interface SchemaFormContext<TData extends FieldValues> {
|
|
|
31
30
|
setError: Dispatch<SetStateAction<unknown>>;
|
|
32
31
|
getUpdatedData: () => TData | Promise<TData>;
|
|
33
32
|
customErrorRenderer?: (error: unknown) => ReactNode;
|
|
34
|
-
validationLocale?: SupportedLocale;
|
|
35
33
|
timezone?: string;
|
|
36
34
|
}
|
|
37
35
|
export declare const SchemaFormContext: import("react").Context<SchemaFormContext<unknown>>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ForeignKeyProps } from "@/components/Form/components/fields/StringInputField";
|
|
2
|
-
import { SupportedLocale } from "@/components/Form/utils/validation";
|
|
3
2
|
import { AxiosRequestConfig } from "axios";
|
|
4
3
|
import { JSONSchema7 } from "json-schema";
|
|
5
4
|
import { Dispatch, ReactNode, SetStateAction } from "react";
|
|
@@ -23,7 +22,6 @@ export interface FormRootProps<TData extends FieldValues> {
|
|
|
23
22
|
requestOptions?: AxiosRequestConfig;
|
|
24
23
|
getUpdatedData?: () => TData | Promise<TData> | void;
|
|
25
24
|
customErrorRenderer?: (error: unknown) => ReactNode;
|
|
26
|
-
validationLocale?: SupportedLocale;
|
|
27
25
|
}
|
|
28
26
|
export interface CustomJSONSchema7Definition extends JSONSchema7 {
|
|
29
27
|
variant: string;
|
|
@@ -40,4 +38,4 @@ export declare const idPickerSanityCheck: (column: string, foreign_key?: {
|
|
|
40
38
|
column?: string | undefined;
|
|
41
39
|
display_column?: string | undefined;
|
|
42
40
|
} | undefined) => void;
|
|
43
|
-
export declare const FormRoot: <TData extends FieldValues>({ schema, idMap, setIdMap, form, serverUrl, translate, children, order, ignore, include, onSubmit, rowNumber, requestOptions, getUpdatedData, customErrorRenderer,
|
|
41
|
+
export declare const FormRoot: <TData extends FieldValues>({ schema, idMap, setIdMap, form, serverUrl, translate, children, order, ignore, include, onSubmit, rowNumber, requestOptions, getUpdatedData, customErrorRenderer, }: FormRootProps<TData>) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,12 +1,67 @@
|
|
|
1
1
|
import { JSONSchema7 } from "json-schema";
|
|
2
|
-
declare const
|
|
3
|
-
en:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
declare const errorMessages: {
|
|
3
|
+
en: {
|
|
4
|
+
required: (field: string) => string;
|
|
5
|
+
format: (format: string) => string;
|
|
6
|
+
type: (expectedType: string, actualType: string) => string;
|
|
7
|
+
minLength: (limit: number) => string;
|
|
8
|
+
maxLength: (limit: number) => string;
|
|
9
|
+
minimum: (limit: number) => string;
|
|
10
|
+
maximum: (limit: number) => string;
|
|
11
|
+
pattern: () => string;
|
|
12
|
+
enum: (allowedValues: string[]) => string;
|
|
13
|
+
default: () => string;
|
|
14
|
+
};
|
|
15
|
+
'zh-HK': {
|
|
16
|
+
required: (field: string) => string;
|
|
17
|
+
format: (format: string) => string;
|
|
18
|
+
type: (expectedType: string, actualType: string) => string;
|
|
19
|
+
minLength: (limit: number) => string;
|
|
20
|
+
maxLength: (limit: number) => string;
|
|
21
|
+
minimum: (limit: number) => string;
|
|
22
|
+
maximum: (limit: number) => string;
|
|
23
|
+
pattern: () => string;
|
|
24
|
+
enum: (allowedValues: string[]) => string;
|
|
25
|
+
default: () => string;
|
|
26
|
+
};
|
|
27
|
+
'zh-TW': {
|
|
28
|
+
required: (field: string) => string;
|
|
29
|
+
format: (format: string) => string;
|
|
30
|
+
type: (expectedType: string, actualType: string) => string;
|
|
31
|
+
minLength: (limit: number) => string;
|
|
32
|
+
maxLength: (limit: number) => string;
|
|
33
|
+
minimum: (limit: number) => string;
|
|
34
|
+
maximum: (limit: number) => string;
|
|
35
|
+
pattern: () => string;
|
|
36
|
+
enum: (allowedValues: string[]) => string;
|
|
37
|
+
default: () => string;
|
|
38
|
+
};
|
|
39
|
+
'zh-CN': {
|
|
40
|
+
required: (field: string) => string;
|
|
41
|
+
format: (format: string) => string;
|
|
42
|
+
type: (expectedType: string, actualType: string) => string;
|
|
43
|
+
minLength: (limit: number) => string;
|
|
44
|
+
maxLength: (limit: number) => string;
|
|
45
|
+
minimum: (limit: number) => string;
|
|
46
|
+
maximum: (limit: number) => string;
|
|
47
|
+
pattern: () => string;
|
|
48
|
+
enum: (allowedValues: string[]) => string;
|
|
49
|
+
default: () => string;
|
|
50
|
+
};
|
|
51
|
+
zh: {
|
|
52
|
+
required: (field: string) => string;
|
|
53
|
+
format: (format: string) => string;
|
|
54
|
+
type: (expectedType: string, actualType: string) => string;
|
|
55
|
+
minLength: (limit: number) => string;
|
|
56
|
+
maxLength: (limit: number) => string;
|
|
57
|
+
minimum: (limit: number) => string;
|
|
58
|
+
maximum: (limit: number) => string;
|
|
59
|
+
pattern: () => string;
|
|
60
|
+
enum: (allowedValues: string[]) => string;
|
|
61
|
+
default: () => string;
|
|
62
|
+
};
|
|
8
63
|
};
|
|
9
|
-
export type SupportedLocale = keyof typeof
|
|
64
|
+
export type SupportedLocale = keyof typeof errorMessages;
|
|
10
65
|
export interface ValidationError {
|
|
11
66
|
field: string;
|
|
12
67
|
message: string;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -121,5 +121,3 @@ export * from "./components/DatePicker/getMultiDates";
|
|
|
121
121
|
export * from "./components/DatePicker/getRangeDates";
|
|
122
122
|
export * from "./components/DatePicker/RangeDatePicker";
|
|
123
123
|
export * from "./components/DataTable/display/RecordDisplay";
|
|
124
|
-
export * from "./components/Form/utils/validation";
|
|
125
|
-
export type { SupportedLocale, ValidationOptions } from "./components/Form/utils/validation";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bsol-oss/react-datatable5",
|
|
3
|
-
"version": "12.0.0-beta.
|
|
3
|
+
"version": "12.0.0-beta.60",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"@tanstack/react-table": "^8.21.2",
|
|
45
45
|
"@uidotdev/usehooks": "^2.4.1",
|
|
46
46
|
"ajv": "^8.12.0",
|
|
47
|
+
"ajv-errors": "^3.0.0",
|
|
47
48
|
"ajv-formats": "^3.0.1",
|
|
48
|
-
"ajv-i18n": "^4.2.0",
|
|
49
49
|
"axios": "^1.7.9",
|
|
50
50
|
"dayjs": "^1.11.13",
|
|
51
51
|
"next-themes": "^0.4.4",
|
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
"@storybook/react": "^8.4.7",
|
|
67
67
|
"@storybook/react-vite": "^8.4.7",
|
|
68
68
|
"@storybook/test": "^8.4.7",
|
|
69
|
+
"@types/ajv-errors": "^2.0.0",
|
|
69
70
|
"@types/json-schema": "^7.0.15",
|
|
70
71
|
"@types/react": "19.0.2",
|
|
71
72
|
"@types/react-beautiful-dnd": "^13.1.8",
|
|
@@ -75,7 +76,7 @@
|
|
|
75
76
|
"@vitejs/plugin-react": "^4.2.1",
|
|
76
77
|
"ajv": "^8.12.0",
|
|
77
78
|
"ajv-formats": "^3.0.1",
|
|
78
|
-
"ajv-
|
|
79
|
+
"ajv-errors": "^3.0.0",
|
|
79
80
|
"eslint": "^8.57.0",
|
|
80
81
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
81
82
|
"eslint-plugin-react-refresh": "^0.4.6",
|