@ram_28/kf-ai-sdk 1.0.5 → 1.0.7
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/components/hooks/useFilter/index.d.ts +1 -2
- package/dist/components/hooks/useFilter/index.d.ts.map +1 -1
- package/dist/components/hooks/useFilter/types.d.ts +1 -18
- package/dist/components/hooks/useFilter/types.d.ts.map +1 -1
- package/dist/components/hooks/useFilter/useFilter.d.ts +1 -1
- package/dist/components/hooks/useFilter/useFilter.d.ts.map +1 -1
- package/dist/components/hooks/useKanban/types.d.ts +1 -4
- package/dist/components/hooks/useKanban/types.d.ts.map +1 -1
- package/dist/components/hooks/useKanban/useKanban.d.ts.map +1 -1
- package/dist/components/hooks/useTable/types.d.ts +1 -3
- package/dist/components/hooks/useTable/types.d.ts.map +1 -1
- package/dist/components/hooks/useTable/useTable.d.ts.map +1 -1
- package/dist/index.cjs +13 -13
- package/dist/index.mjs +2345 -2577
- package/package.json +1 -1
- package/sdk/components/hooks/useFilter/index.ts +0 -13
- package/sdk/components/hooks/useFilter/types.ts +1 -16
- package/sdk/components/hooks/useFilter/useFilter.ts +6 -27
- package/sdk/components/hooks/useKanban/types.ts +0 -5
- package/sdk/components/hooks/useKanban/useKanban.ts +0 -2
- package/sdk/components/hooks/useTable/types.ts +0 -3
- package/sdk/components/hooks/useTable/useTable.ts +0 -1
- package/dist/components/hooks/useFilter/validation.utils.d.ts +0 -38
- package/dist/components/hooks/useFilter/validation.utils.d.ts.map +0 -1
- package/sdk/components/hooks/useFilter/validation.utils.ts +0 -401
package/package.json
CHANGED
|
@@ -6,25 +6,12 @@ export type {
|
|
|
6
6
|
FilterConditionWithId,
|
|
7
7
|
TypedFilterConditionInput,
|
|
8
8
|
FilterState,
|
|
9
|
-
FieldDefinition,
|
|
10
9
|
ValidationResult,
|
|
11
10
|
ValidationError,
|
|
12
11
|
UseFilterOptions,
|
|
13
12
|
UseFilterReturn
|
|
14
13
|
} from './types';
|
|
15
14
|
|
|
16
|
-
// Validation utilities
|
|
17
|
-
export {
|
|
18
|
-
validateNumberValue,
|
|
19
|
-
validateDateValue,
|
|
20
|
-
validateCurrencyValue,
|
|
21
|
-
validateStringValue,
|
|
22
|
-
validateBooleanValue,
|
|
23
|
-
validateSelectValue,
|
|
24
|
-
getDefaultFieldDefinition,
|
|
25
|
-
createFieldDefinitionsFromSample
|
|
26
|
-
} from './validation.utils';
|
|
27
|
-
|
|
28
15
|
// Payload building utilities
|
|
29
16
|
export {
|
|
30
17
|
buildFilterPayload,
|
|
@@ -51,19 +51,6 @@ export interface FilterState {
|
|
|
51
51
|
conditions: FilterConditionWithId[];
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
export interface FieldDefinition {
|
|
55
|
-
/** Field data type */
|
|
56
|
-
type: 'string' | 'number' | 'date' | 'boolean' | 'currency' | 'select';
|
|
57
|
-
/** Operators allowed for this field type */
|
|
58
|
-
allowedOperators: FilterOperator[];
|
|
59
|
-
/** Custom value validation function */
|
|
60
|
-
validateValue?: (value: any, operator: FilterOperator) => ValidationResult;
|
|
61
|
-
/** Value transformation function */
|
|
62
|
-
transformValue?: (value: any) => any;
|
|
63
|
-
/** Options for select fields */
|
|
64
|
-
selectOptions?: Array<{ label: string; value: any }>;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
54
|
export interface ValidationResult {
|
|
68
55
|
/** Whether the validation passed */
|
|
69
56
|
isValid: boolean;
|
|
@@ -80,13 +67,11 @@ export interface ValidationError {
|
|
|
80
67
|
message: string;
|
|
81
68
|
}
|
|
82
69
|
|
|
83
|
-
export interface UseFilterOptions
|
|
70
|
+
export interface UseFilterOptions {
|
|
84
71
|
/** Initial filter conditions */
|
|
85
72
|
initialConditions?: FilterConditionWithId[];
|
|
86
73
|
/** Initial logical operator */
|
|
87
74
|
initialLogicalOperator?: LogicalOperator;
|
|
88
|
-
/** Field definitions for validation */
|
|
89
|
-
fieldDefinitions?: Record<keyof T, FieldDefinition>;
|
|
90
75
|
/** Whether to validate conditions on change */
|
|
91
76
|
validateOnChange?: boolean;
|
|
92
77
|
/** Callback when condition is added */
|
|
@@ -3,7 +3,6 @@ import type { Filter, LogicalOperator } from "../../../types/common";
|
|
|
3
3
|
import type {
|
|
4
4
|
FilterConditionWithId,
|
|
5
5
|
TypedFilterConditionInput,
|
|
6
|
-
FieldDefinition,
|
|
7
6
|
ValidationResult,
|
|
8
7
|
ValidationError,
|
|
9
8
|
FilterState,
|
|
@@ -32,9 +31,8 @@ const isLogicalOperator = (operator: string): operator is LogicalOperator => {
|
|
|
32
31
|
/**
|
|
33
32
|
* Validate a filter condition (supports both simple conditions and nested logical groups)
|
|
34
33
|
*/
|
|
35
|
-
const validateFilterCondition =
|
|
36
|
-
condition: Partial<FilterConditionWithId
|
|
37
|
-
fieldDefinitions?: Record<keyof T, FieldDefinition>
|
|
34
|
+
const validateFilterCondition = (
|
|
35
|
+
condition: Partial<FilterConditionWithId>
|
|
38
36
|
): ValidationResult => {
|
|
39
37
|
const errors: string[] = [];
|
|
40
38
|
|
|
@@ -57,7 +55,7 @@ const validateFilterCondition = <T>(
|
|
|
57
55
|
// Recursively validate children
|
|
58
56
|
if (condition.children && Array.isArray(condition.children)) {
|
|
59
57
|
condition.children.forEach((child, index) => {
|
|
60
|
-
const childValidation = validateFilterCondition(child
|
|
58
|
+
const childValidation = validateFilterCondition(child);
|
|
61
59
|
if (!childValidation.isValid) {
|
|
62
60
|
errors.push(...childValidation.errors.map(err => `Child ${index + 1}: ${err}`));
|
|
63
61
|
}
|
|
@@ -104,25 +102,6 @@ const validateFilterCondition = <T>(
|
|
|
104
102
|
}
|
|
105
103
|
}
|
|
106
104
|
|
|
107
|
-
// Field-specific validation
|
|
108
|
-
if (fieldDefinitions && condition.lhsField && condition.operator) {
|
|
109
|
-
const fieldDef = fieldDefinitions[condition.lhsField as keyof T];
|
|
110
|
-
if (fieldDef) {
|
|
111
|
-
// Check if operator is allowed for this field
|
|
112
|
-
if (!fieldDef.allowedOperators.includes(condition.operator as any)) {
|
|
113
|
-
errors.push(`Operator ${condition.operator} is not allowed for field ${condition.lhsField}`);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Custom field validation
|
|
117
|
-
if (fieldDef.validateValue && condition.rhsValue !== undefined) {
|
|
118
|
-
const fieldValidation = fieldDef.validateValue(condition.rhsValue, condition.operator as any);
|
|
119
|
-
if (!fieldValidation.isValid) {
|
|
120
|
-
errors.push(...fieldValidation.errors);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
105
|
// Simple conditions should not have children
|
|
127
106
|
if (condition.children && condition.children.length > 0) {
|
|
128
107
|
errors.push('Condition operators should not have children');
|
|
@@ -187,7 +166,7 @@ const buildFilterPayload = (
|
|
|
187
166
|
// ============================================================
|
|
188
167
|
|
|
189
168
|
export function useFilter<T = any>(
|
|
190
|
-
options: UseFilterOptions
|
|
169
|
+
options: UseFilterOptions = {}
|
|
191
170
|
): UseFilterReturn<T> {
|
|
192
171
|
// ============================================================
|
|
193
172
|
// STATE MANAGEMENT
|
|
@@ -209,8 +188,8 @@ export function useFilter<T = any>(
|
|
|
209
188
|
// ============================================================
|
|
210
189
|
|
|
211
190
|
const validateCondition = useCallback((condition: Partial<FilterConditionWithId>): ValidationResult => {
|
|
212
|
-
return validateFilterCondition(condition
|
|
213
|
-
}, [
|
|
191
|
+
return validateFilterCondition(condition);
|
|
192
|
+
}, []);
|
|
214
193
|
|
|
215
194
|
const validateAllConditions = useCallback((): ValidationResult => {
|
|
216
195
|
const allErrors: string[] = [];
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
import type {
|
|
8
8
|
FilterConditionWithId,
|
|
9
9
|
ValidationError,
|
|
10
|
-
FieldDefinition,
|
|
11
10
|
} from "../useFilter";
|
|
12
11
|
import type { LogicalOperator } from "../../../types/common";
|
|
13
12
|
|
|
@@ -199,9 +198,6 @@ export interface UseKanbanOptions<T> {
|
|
|
199
198
|
/** Enable search functionality */
|
|
200
199
|
enableSearch?: boolean;
|
|
201
200
|
|
|
202
|
-
/** Field definitions for card validation */
|
|
203
|
-
cardFieldDefinitions?: Record<keyof T, FieldDefinition>;
|
|
204
|
-
|
|
205
201
|
/** Initial state */
|
|
206
202
|
initialState?: {
|
|
207
203
|
/** Initial filter conditions */
|
|
@@ -537,7 +533,6 @@ export interface CardValidationContext<T> {
|
|
|
537
533
|
card: Partial<KanbanCard<T>>;
|
|
538
534
|
column?: KanbanColumn<T>;
|
|
539
535
|
allColumns: KanbanColumn<T>[];
|
|
540
|
-
fieldDefinitions?: Record<keyof T, FieldDefinition>;
|
|
541
536
|
}
|
|
542
537
|
|
|
543
538
|
/**
|
|
@@ -28,7 +28,6 @@ export function useKanban<T extends Record<string, any> = Record<string, any>>(
|
|
|
28
28
|
cardSource,
|
|
29
29
|
source,
|
|
30
30
|
enableDragDrop = true,
|
|
31
|
-
cardFieldDefinitions,
|
|
32
31
|
initialState,
|
|
33
32
|
onCardMove,
|
|
34
33
|
onCardCreate,
|
|
@@ -93,7 +92,6 @@ export function useKanban<T extends Record<string, any> = Record<string, any>>(
|
|
|
93
92
|
const filterHook = useFilter<T>({
|
|
94
93
|
initialConditions: initialState?.filters,
|
|
95
94
|
initialLogicalOperator: initialState?.filterOperator || "And",
|
|
96
|
-
fieldDefinitions: cardFieldDefinitions,
|
|
97
95
|
validateOnChange: true,
|
|
98
96
|
onValidationError: onFilterError,
|
|
99
97
|
});
|
|
@@ -3,7 +3,6 @@ import type {
|
|
|
3
3
|
FilterConditionWithId,
|
|
4
4
|
TypedFilterConditionInput,
|
|
5
5
|
ValidationError,
|
|
6
|
-
FieldDefinition,
|
|
7
6
|
} from "../useFilter";
|
|
8
7
|
|
|
9
8
|
// ============================================================
|
|
@@ -34,8 +33,6 @@ export interface UseTableOptions<T> {
|
|
|
34
33
|
enableFiltering?: boolean;
|
|
35
34
|
/** Enable pagination */
|
|
36
35
|
enablePagination?: boolean;
|
|
37
|
-
/** Field definitions for filter validation */
|
|
38
|
-
fieldDefinitions?: Record<keyof T, FieldDefinition>;
|
|
39
36
|
/** Initial state */
|
|
40
37
|
initialState?: {
|
|
41
38
|
pagination?: {
|
|
@@ -92,7 +92,6 @@ export function useTable<T = any>(
|
|
|
92
92
|
const filterHook = useFilter<T>({
|
|
93
93
|
initialConditions: options.initialState?.filters,
|
|
94
94
|
initialLogicalOperator: options.initialState?.filterOperator || "And",
|
|
95
|
-
fieldDefinitions: options.fieldDefinitions,
|
|
96
95
|
validateOnChange: true,
|
|
97
96
|
onValidationError: options.onFilterError,
|
|
98
97
|
onConditionAdd: () => {
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import type { FilterOperator } from "../../../types/common";
|
|
2
|
-
import type { FieldDefinition, ValidationResult } from "../useFilter";
|
|
3
|
-
/**
|
|
4
|
-
* Validate number values based on operator
|
|
5
|
-
*/
|
|
6
|
-
export declare const validateNumberValue: (value: any, operator: FilterOperator) => ValidationResult;
|
|
7
|
-
/**
|
|
8
|
-
* Validate date values based on operator
|
|
9
|
-
*/
|
|
10
|
-
export declare const validateDateValue: (value: any, operator: FilterOperator) => ValidationResult;
|
|
11
|
-
/**
|
|
12
|
-
* Validate currency values based on operator
|
|
13
|
-
*/
|
|
14
|
-
export declare const validateCurrencyValue: (value: any, operator: FilterOperator) => ValidationResult;
|
|
15
|
-
/**
|
|
16
|
-
* Validate string values based on operator
|
|
17
|
-
*/
|
|
18
|
-
export declare const validateStringValue: (value: any, operator: FilterOperator) => ValidationResult;
|
|
19
|
-
/**
|
|
20
|
-
* Validate boolean values based on operator
|
|
21
|
-
*/
|
|
22
|
-
export declare const validateBooleanValue: (value: any, operator: FilterOperator) => ValidationResult;
|
|
23
|
-
/**
|
|
24
|
-
* Validate select field values based on operator and available options
|
|
25
|
-
*/
|
|
26
|
-
export declare const validateSelectValue: (value: any, operator: FilterOperator, selectOptions?: Array<{
|
|
27
|
-
label: string;
|
|
28
|
-
value: any;
|
|
29
|
-
}>) => ValidationResult;
|
|
30
|
-
/**
|
|
31
|
-
* Get default field definition based on field type
|
|
32
|
-
*/
|
|
33
|
-
export declare const getDefaultFieldDefinition: (fieldType: FieldDefinition["type"]) => FieldDefinition;
|
|
34
|
-
/**
|
|
35
|
-
* Create field definitions from sample data
|
|
36
|
-
*/
|
|
37
|
-
export declare const createFieldDefinitionsFromSample: <T>(sampleData: T) => Record<keyof T, FieldDefinition>;
|
|
38
|
-
//# sourceMappingURL=validation.utils.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"validation.utils.d.ts","sourceRoot":"","sources":["../../../../sdk/components/hooks/useFilter/validation.utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAMtE;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAAI,OAAO,GAAG,EAAE,UAAU,cAAc,KAAG,gBAwC1E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,GAAG,EAAE,UAAU,cAAc,KAAG,gBAqDxE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO,GAAG,EAAE,UAAU,cAAc,KAAG,gBAmD5E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAAI,OAAO,GAAG,EAAE,UAAU,cAAc,KAAG,gBA+C1E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,GAAI,OAAO,GAAG,EAAE,UAAU,cAAc,KAAG,gBAkC3E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAC9B,OAAO,GAAG,EACV,UAAU,cAAc,EACxB,gBAAgB,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,GAAG,CAAA;CAAE,CAAC,KACnD,gBA2CF,CAAC;AAMF;;GAEG;AACH,eAAO,MAAM,yBAAyB,GAAI,WAAW,eAAe,CAAC,MAAM,CAAC,KAAG,eAmD9E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,gCAAgC,GAAI,CAAC,EAAE,YAAY,CAAC,KAAG,MAAM,CAAC,MAAM,CAAC,EAAE,eAAe,CA4BlG,CAAC"}
|
|
@@ -1,401 +0,0 @@
|
|
|
1
|
-
import type { FilterOperator } from "../../../types/common";
|
|
2
|
-
import type { FieldDefinition, ValidationResult } from "../useFilter";
|
|
3
|
-
|
|
4
|
-
// ============================================================
|
|
5
|
-
// FIELD TYPE VALIDATION HELPERS
|
|
6
|
-
// ============================================================
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Validate number values based on operator
|
|
10
|
-
*/
|
|
11
|
-
export const validateNumberValue = (value: any, operator: FilterOperator): ValidationResult => {
|
|
12
|
-
const errors: string[] = [];
|
|
13
|
-
|
|
14
|
-
switch (operator) {
|
|
15
|
-
case 'Between':
|
|
16
|
-
case 'NotBetween':
|
|
17
|
-
if (!Array.isArray(value) || value.length !== 2) {
|
|
18
|
-
errors.push('Between operators require exactly two numeric values');
|
|
19
|
-
} else {
|
|
20
|
-
const [min, max] = value;
|
|
21
|
-
if (typeof min !== 'number' || typeof max !== 'number') {
|
|
22
|
-
errors.push('Between values must be numbers');
|
|
23
|
-
} else if (min >= max) {
|
|
24
|
-
errors.push('First value must be less than second value');
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
break;
|
|
28
|
-
case 'IN':
|
|
29
|
-
case 'NIN':
|
|
30
|
-
if (!Array.isArray(value) || value.length === 0) {
|
|
31
|
-
errors.push('IN/NIN operators require a non-empty array of numbers');
|
|
32
|
-
} else if (!value.every((v: any) => typeof v === 'number')) {
|
|
33
|
-
errors.push('All values in array must be numbers');
|
|
34
|
-
}
|
|
35
|
-
break;
|
|
36
|
-
case 'Empty':
|
|
37
|
-
case 'NotEmpty':
|
|
38
|
-
// No validation needed for these operators
|
|
39
|
-
break;
|
|
40
|
-
default:
|
|
41
|
-
if (typeof value !== 'number' || isNaN(value)) {
|
|
42
|
-
errors.push('Value must be a valid number');
|
|
43
|
-
}
|
|
44
|
-
break;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return {
|
|
48
|
-
isValid: errors.length === 0,
|
|
49
|
-
errors
|
|
50
|
-
};
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Validate date values based on operator
|
|
55
|
-
*/
|
|
56
|
-
export const validateDateValue = (value: any, operator: FilterOperator): ValidationResult => {
|
|
57
|
-
const errors: string[] = [];
|
|
58
|
-
|
|
59
|
-
const isValidDate = (date: any): boolean => {
|
|
60
|
-
return date instanceof Date && !isNaN(date.getTime());
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
const parseDate = (val: any): Date | null => {
|
|
64
|
-
if (val instanceof Date) return val;
|
|
65
|
-
if (typeof val === 'string' || typeof val === 'number') {
|
|
66
|
-
const parsed = new Date(val);
|
|
67
|
-
return isValidDate(parsed) ? parsed : null;
|
|
68
|
-
}
|
|
69
|
-
return null;
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
switch (operator) {
|
|
73
|
-
case 'Between':
|
|
74
|
-
case 'NotBetween':
|
|
75
|
-
if (!Array.isArray(value) || value.length !== 2) {
|
|
76
|
-
errors.push('Between operators require exactly two date values');
|
|
77
|
-
} else {
|
|
78
|
-
const [start, end] = value.map(parseDate);
|
|
79
|
-
if (!start || !end) {
|
|
80
|
-
errors.push('Between values must be valid dates');
|
|
81
|
-
} else if (start >= end) {
|
|
82
|
-
errors.push('Start date must be before end date');
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
break;
|
|
86
|
-
case 'IN':
|
|
87
|
-
case 'NIN':
|
|
88
|
-
if (!Array.isArray(value) || value.length === 0) {
|
|
89
|
-
errors.push('IN/NIN operators require a non-empty array of dates');
|
|
90
|
-
} else if (!value.every((v: any) => parseDate(v))) {
|
|
91
|
-
errors.push('All values in array must be valid dates');
|
|
92
|
-
}
|
|
93
|
-
break;
|
|
94
|
-
case 'Empty':
|
|
95
|
-
case 'NotEmpty':
|
|
96
|
-
// No validation needed for these operators
|
|
97
|
-
break;
|
|
98
|
-
default:
|
|
99
|
-
if (!parseDate(value)) {
|
|
100
|
-
errors.push('Value must be a valid date');
|
|
101
|
-
}
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return {
|
|
106
|
-
isValid: errors.length === 0,
|
|
107
|
-
errors
|
|
108
|
-
};
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Validate currency values based on operator
|
|
113
|
-
*/
|
|
114
|
-
export const validateCurrencyValue = (value: any, operator: FilterOperator): ValidationResult => {
|
|
115
|
-
const errors: string[] = [];
|
|
116
|
-
|
|
117
|
-
const isValidCurrencyObject = (val: any): boolean => {
|
|
118
|
-
return val &&
|
|
119
|
-
typeof val === 'object' &&
|
|
120
|
-
typeof val.value === 'number' &&
|
|
121
|
-
typeof val.currency === 'string' &&
|
|
122
|
-
val.currency.length === 3; // Standard currency codes are 3 letters
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
const isValidCurrencyString = (val: any): boolean => {
|
|
126
|
-
return typeof val === 'string' && /^\d+(\.\d{2})?\s[A-Z]{3}$/.test(val);
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
const isValidCurrency = (val: any): boolean => {
|
|
130
|
-
return isValidCurrencyObject(val) || isValidCurrencyString(val) || typeof val === 'number';
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
switch (operator) {
|
|
134
|
-
case 'Between':
|
|
135
|
-
case 'NotBetween':
|
|
136
|
-
if (!Array.isArray(value) || value.length !== 2) {
|
|
137
|
-
errors.push('Between operators require exactly two currency values');
|
|
138
|
-
} else if (!value.every(isValidCurrency)) {
|
|
139
|
-
errors.push('Between values must be valid currency amounts');
|
|
140
|
-
}
|
|
141
|
-
break;
|
|
142
|
-
case 'IN':
|
|
143
|
-
case 'NIN':
|
|
144
|
-
if (!Array.isArray(value) || value.length === 0) {
|
|
145
|
-
errors.push('IN/NIN operators require a non-empty array of currency values');
|
|
146
|
-
} else if (!value.every(isValidCurrency)) {
|
|
147
|
-
errors.push('All values in array must be valid currency amounts');
|
|
148
|
-
}
|
|
149
|
-
break;
|
|
150
|
-
case 'Empty':
|
|
151
|
-
case 'NotEmpty':
|
|
152
|
-
// No validation needed for these operators
|
|
153
|
-
break;
|
|
154
|
-
default:
|
|
155
|
-
if (!isValidCurrency(value)) {
|
|
156
|
-
errors.push('Value must be a valid currency amount');
|
|
157
|
-
}
|
|
158
|
-
break;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return {
|
|
162
|
-
isValid: errors.length === 0,
|
|
163
|
-
errors
|
|
164
|
-
};
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Validate string values based on operator
|
|
169
|
-
*/
|
|
170
|
-
export const validateStringValue = (value: any, operator: FilterOperator): ValidationResult => {
|
|
171
|
-
const errors: string[] = [];
|
|
172
|
-
|
|
173
|
-
switch (operator) {
|
|
174
|
-
case 'Between':
|
|
175
|
-
case 'NotBetween':
|
|
176
|
-
errors.push('Between operators are not supported for string fields');
|
|
177
|
-
break;
|
|
178
|
-
case 'GT':
|
|
179
|
-
case 'GTE':
|
|
180
|
-
case 'LT':
|
|
181
|
-
case 'LTE':
|
|
182
|
-
errors.push('Comparison operators are not supported for string fields');
|
|
183
|
-
break;
|
|
184
|
-
case 'IN':
|
|
185
|
-
case 'NIN':
|
|
186
|
-
if (!Array.isArray(value) || value.length === 0) {
|
|
187
|
-
errors.push('IN/NIN operators require a non-empty array of strings');
|
|
188
|
-
} else if (!value.every((v: any) => typeof v === 'string')) {
|
|
189
|
-
errors.push('All values in array must be strings');
|
|
190
|
-
}
|
|
191
|
-
break;
|
|
192
|
-
case 'MinLength':
|
|
193
|
-
if (typeof value !== 'number' || value < 0) {
|
|
194
|
-
errors.push('MinLength value must be a non-negative number');
|
|
195
|
-
}
|
|
196
|
-
break;
|
|
197
|
-
case 'MaxLength':
|
|
198
|
-
if (typeof value !== 'number' || value < 0) {
|
|
199
|
-
errors.push('MaxLength value must be a non-negative number');
|
|
200
|
-
}
|
|
201
|
-
break;
|
|
202
|
-
case 'Empty':
|
|
203
|
-
case 'NotEmpty':
|
|
204
|
-
// No validation needed for these operators
|
|
205
|
-
break;
|
|
206
|
-
default:
|
|
207
|
-
if (typeof value !== 'string') {
|
|
208
|
-
errors.push('Value must be a string');
|
|
209
|
-
}
|
|
210
|
-
break;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return {
|
|
214
|
-
isValid: errors.length === 0,
|
|
215
|
-
errors
|
|
216
|
-
};
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Validate boolean values based on operator
|
|
221
|
-
*/
|
|
222
|
-
export const validateBooleanValue = (value: any, operator: FilterOperator): ValidationResult => {
|
|
223
|
-
const errors: string[] = [];
|
|
224
|
-
|
|
225
|
-
const supportedOperators: FilterOperator[] = ['EQ', 'NE', 'IN', 'NIN', 'Empty', 'NotEmpty'];
|
|
226
|
-
|
|
227
|
-
if (!supportedOperators.includes(operator)) {
|
|
228
|
-
errors.push(`Operator ${operator} is not supported for boolean fields`);
|
|
229
|
-
return { isValid: false, errors };
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
switch (operator) {
|
|
233
|
-
case 'IN':
|
|
234
|
-
case 'NIN':
|
|
235
|
-
if (!Array.isArray(value) || value.length === 0) {
|
|
236
|
-
errors.push('IN/NIN operators require a non-empty array of boolean values');
|
|
237
|
-
} else if (!value.every((v: any) => typeof v === 'boolean')) {
|
|
238
|
-
errors.push('All values in array must be boolean');
|
|
239
|
-
}
|
|
240
|
-
break;
|
|
241
|
-
case 'Empty':
|
|
242
|
-
case 'NotEmpty':
|
|
243
|
-
// No validation needed for these operators
|
|
244
|
-
break;
|
|
245
|
-
default:
|
|
246
|
-
if (typeof value !== 'boolean') {
|
|
247
|
-
errors.push('Value must be a boolean (true or false)');
|
|
248
|
-
}
|
|
249
|
-
break;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return {
|
|
253
|
-
isValid: errors.length === 0,
|
|
254
|
-
errors
|
|
255
|
-
};
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Validate select field values based on operator and available options
|
|
260
|
-
*/
|
|
261
|
-
export const validateSelectValue = (
|
|
262
|
-
value: any,
|
|
263
|
-
operator: FilterOperator,
|
|
264
|
-
selectOptions?: Array<{ label: string; value: any }>
|
|
265
|
-
): ValidationResult => {
|
|
266
|
-
const errors: string[] = [];
|
|
267
|
-
|
|
268
|
-
if (!selectOptions || selectOptions.length === 0) {
|
|
269
|
-
errors.push('No select options defined for this field');
|
|
270
|
-
return { isValid: false, errors };
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
const validValues = selectOptions.map(option => option.value);
|
|
274
|
-
const isValidOption = (val: any) => validValues.includes(val);
|
|
275
|
-
|
|
276
|
-
switch (operator) {
|
|
277
|
-
case 'Between':
|
|
278
|
-
case 'NotBetween':
|
|
279
|
-
case 'GT':
|
|
280
|
-
case 'GTE':
|
|
281
|
-
case 'LT':
|
|
282
|
-
case 'LTE':
|
|
283
|
-
errors.push(`Operator ${operator} is not supported for select fields`);
|
|
284
|
-
break;
|
|
285
|
-
case 'IN':
|
|
286
|
-
case 'NIN':
|
|
287
|
-
if (!Array.isArray(value) || value.length === 0) {
|
|
288
|
-
errors.push('IN/NIN operators require a non-empty array of values');
|
|
289
|
-
} else if (!value.every(isValidOption)) {
|
|
290
|
-
errors.push('All values must be from the available options');
|
|
291
|
-
}
|
|
292
|
-
break;
|
|
293
|
-
case 'Empty':
|
|
294
|
-
case 'NotEmpty':
|
|
295
|
-
// No validation needed for these operators
|
|
296
|
-
break;
|
|
297
|
-
default:
|
|
298
|
-
if (!isValidOption(value)) {
|
|
299
|
-
errors.push('Value must be one of the available options');
|
|
300
|
-
}
|
|
301
|
-
break;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
return {
|
|
305
|
-
isValid: errors.length === 0,
|
|
306
|
-
errors
|
|
307
|
-
};
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
// ============================================================
|
|
311
|
-
// FIELD DEFINITION HELPERS
|
|
312
|
-
// ============================================================
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Get default field definition based on field type
|
|
316
|
-
*/
|
|
317
|
-
export const getDefaultFieldDefinition = (fieldType: FieldDefinition['type']): FieldDefinition => {
|
|
318
|
-
switch (fieldType) {
|
|
319
|
-
case 'string':
|
|
320
|
-
return {
|
|
321
|
-
type: 'string',
|
|
322
|
-
allowedOperators: ['EQ', 'NE', 'Contains', 'NotContains', 'IN', 'NIN', 'Empty', 'NotEmpty', 'MinLength', 'MaxLength'],
|
|
323
|
-
validateValue: validateStringValue
|
|
324
|
-
};
|
|
325
|
-
|
|
326
|
-
case 'number':
|
|
327
|
-
return {
|
|
328
|
-
type: 'number',
|
|
329
|
-
allowedOperators: ['EQ', 'NE', 'GT', 'GTE', 'LT', 'LTE', 'Between', 'NotBetween', 'IN', 'NIN', 'Empty', 'NotEmpty'],
|
|
330
|
-
validateValue: validateNumberValue
|
|
331
|
-
};
|
|
332
|
-
|
|
333
|
-
case 'date':
|
|
334
|
-
return {
|
|
335
|
-
type: 'date',
|
|
336
|
-
allowedOperators: ['EQ', 'NE', 'GT', 'GTE', 'LT', 'LTE', 'Between', 'NotBetween', 'IN', 'NIN', 'Empty', 'NotEmpty'],
|
|
337
|
-
validateValue: validateDateValue
|
|
338
|
-
};
|
|
339
|
-
|
|
340
|
-
case 'boolean':
|
|
341
|
-
return {
|
|
342
|
-
type: 'boolean',
|
|
343
|
-
allowedOperators: ['EQ', 'NE', 'IN', 'NIN', 'Empty', 'NotEmpty'],
|
|
344
|
-
validateValue: validateBooleanValue
|
|
345
|
-
};
|
|
346
|
-
|
|
347
|
-
case 'currency':
|
|
348
|
-
return {
|
|
349
|
-
type: 'currency',
|
|
350
|
-
allowedOperators: ['EQ', 'NE', 'GT', 'GTE', 'LT', 'LTE', 'Between', 'NotBetween', 'IN', 'NIN', 'Empty', 'NotEmpty'],
|
|
351
|
-
validateValue: validateCurrencyValue
|
|
352
|
-
};
|
|
353
|
-
|
|
354
|
-
case 'select':
|
|
355
|
-
const selectFieldDef: FieldDefinition = {
|
|
356
|
-
type: 'select',
|
|
357
|
-
allowedOperators: ['EQ', 'NE', 'IN', 'NIN', 'Empty', 'NotEmpty'],
|
|
358
|
-
selectOptions: []
|
|
359
|
-
};
|
|
360
|
-
selectFieldDef.validateValue = (value: any, operator: FilterOperator) =>
|
|
361
|
-
validateSelectValue(value, operator, selectFieldDef.selectOptions);
|
|
362
|
-
return selectFieldDef;
|
|
363
|
-
|
|
364
|
-
default:
|
|
365
|
-
// Default to string type
|
|
366
|
-
return getDefaultFieldDefinition('string');
|
|
367
|
-
}
|
|
368
|
-
};
|
|
369
|
-
|
|
370
|
-
/**
|
|
371
|
-
* Create field definitions from sample data
|
|
372
|
-
*/
|
|
373
|
-
export const createFieldDefinitionsFromSample = <T>(sampleData: T): Record<keyof T, FieldDefinition> => {
|
|
374
|
-
const fieldDefinitions = {} as Record<keyof T, FieldDefinition>;
|
|
375
|
-
|
|
376
|
-
if (!sampleData || typeof sampleData !== 'object') {
|
|
377
|
-
return fieldDefinitions;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
Object.keys(sampleData).forEach(key => {
|
|
381
|
-
const value = (sampleData as any)[key];
|
|
382
|
-
const fieldKey = key as keyof T;
|
|
383
|
-
|
|
384
|
-
if (typeof value === 'string') {
|
|
385
|
-
fieldDefinitions[fieldKey] = getDefaultFieldDefinition('string');
|
|
386
|
-
} else if (typeof value === 'number') {
|
|
387
|
-
fieldDefinitions[fieldKey] = getDefaultFieldDefinition('number');
|
|
388
|
-
} else if (typeof value === 'boolean') {
|
|
389
|
-
fieldDefinitions[fieldKey] = getDefaultFieldDefinition('boolean');
|
|
390
|
-
} else if (value instanceof Date) {
|
|
391
|
-
fieldDefinitions[fieldKey] = getDefaultFieldDefinition('date');
|
|
392
|
-
} else if (value && typeof value === 'object' && 'value' in value && 'currency' in value) {
|
|
393
|
-
fieldDefinitions[fieldKey] = getDefaultFieldDefinition('currency');
|
|
394
|
-
} else {
|
|
395
|
-
// Default to string for complex types
|
|
396
|
-
fieldDefinitions[fieldKey] = getDefaultFieldDefinition('string');
|
|
397
|
-
}
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
return fieldDefinitions;
|
|
401
|
-
};
|