@ram_28/kf-ai-sdk 1.0.0
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/LICENSE +21 -0
- package/README.md +840 -0
- package/dist/api/client.d.ts +78 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/datetime.d.ts +21 -0
- package/dist/api/datetime.d.ts.map +1 -0
- package/dist/api/index.d.ts +7 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/metadata.d.ts +75 -0
- package/dist/api/metadata.d.ts.map +1 -0
- package/dist/components/hooks/index.d.ts +8 -0
- package/dist/components/hooks/index.d.ts.map +1 -0
- package/dist/components/hooks/useFilter/index.d.ts +5 -0
- package/dist/components/hooks/useFilter/index.d.ts.map +1 -0
- package/dist/components/hooks/useFilter/payloadBuilder.utils.d.ts +33 -0
- package/dist/components/hooks/useFilter/payloadBuilder.utils.d.ts.map +1 -0
- package/dist/components/hooks/useFilter/types.d.ts +137 -0
- package/dist/components/hooks/useFilter/types.d.ts.map +1 -0
- package/dist/components/hooks/useFilter/useFilter.d.ts +3 -0
- package/dist/components/hooks/useFilter/useFilter.d.ts.map +1 -0
- package/dist/components/hooks/useFilter/validation.utils.d.ts +38 -0
- package/dist/components/hooks/useFilter/validation.utils.d.ts.map +1 -0
- package/dist/components/hooks/useForm/apiClient.d.ts +71 -0
- package/dist/components/hooks/useForm/apiClient.d.ts.map +1 -0
- package/dist/components/hooks/useForm/expressionValidator.utils.d.ts +28 -0
- package/dist/components/hooks/useForm/expressionValidator.utils.d.ts.map +1 -0
- package/dist/components/hooks/useForm/index.d.ts +6 -0
- package/dist/components/hooks/useForm/index.d.ts.map +1 -0
- package/dist/components/hooks/useForm/optimizedExpressionValidator.utils.d.ts +88 -0
- package/dist/components/hooks/useForm/optimizedExpressionValidator.utils.d.ts.map +1 -0
- package/dist/components/hooks/useForm/ruleClassifier.utils.d.ts +28 -0
- package/dist/components/hooks/useForm/ruleClassifier.utils.d.ts.map +1 -0
- package/dist/components/hooks/useForm/schemaParser.utils.d.ts +29 -0
- package/dist/components/hooks/useForm/schemaParser.utils.d.ts.map +1 -0
- package/dist/components/hooks/useForm/types.d.ts +412 -0
- package/dist/components/hooks/useForm/types.d.ts.map +1 -0
- package/dist/components/hooks/useForm/useForm.d.ts +3 -0
- package/dist/components/hooks/useForm/useForm.d.ts.map +1 -0
- package/dist/components/hooks/useKanban/apiClient.d.ts +99 -0
- package/dist/components/hooks/useKanban/apiClient.d.ts.map +1 -0
- package/dist/components/hooks/useKanban/context.d.ts +4 -0
- package/dist/components/hooks/useKanban/context.d.ts.map +1 -0
- package/dist/components/hooks/useKanban/dragDropManager.d.ts +27 -0
- package/dist/components/hooks/useKanban/dragDropManager.d.ts.map +1 -0
- package/dist/components/hooks/useKanban/index.d.ts +6 -0
- package/dist/components/hooks/useKanban/index.d.ts.map +1 -0
- package/dist/components/hooks/useKanban/types.d.ts +438 -0
- package/dist/components/hooks/useKanban/types.d.ts.map +1 -0
- package/dist/components/hooks/useKanban/useKanban.d.ts +3 -0
- package/dist/components/hooks/useKanban/useKanban.d.ts.map +1 -0
- package/dist/components/hooks/useKanban/useKanbanSimple.d.ts +62 -0
- package/dist/components/hooks/useKanban/useKanbanSimple.d.ts.map +1 -0
- package/dist/components/hooks/useTable/index.d.ts +3 -0
- package/dist/components/hooks/useTable/index.d.ts.map +1 -0
- package/dist/components/hooks/useTable/types.d.ts +107 -0
- package/dist/components/hooks/useTable/types.d.ts.map +1 -0
- package/dist/components/hooks/useTable/useTable.d.ts +8 -0
- package/dist/components/hooks/useTable/useTable.d.ts.map +1 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/ui/index.d.ts +2 -0
- package/dist/components/ui/index.d.ts.map +1 -0
- package/dist/components/ui/kanban/Kanban.d.ts +12 -0
- package/dist/components/ui/kanban/Kanban.d.ts.map +1 -0
- package/dist/components/ui/kanban/index.d.ts +2 -0
- package/dist/components/ui/kanban/index.d.ts.map +1 -0
- package/dist/index.cjs +45 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +6522 -0
- package/dist/types/base-fields.d.ts +182 -0
- package/dist/types/base-fields.d.ts.map +1 -0
- package/dist/types/common.d.ts +238 -0
- package/dist/types/common.d.ts.map +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/utils/cn.d.ts +7 -0
- package/dist/utils/cn.d.ts.map +1 -0
- package/dist/utils/formatting.d.ts +52 -0
- package/dist/utils/formatting.d.ts.map +1 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/package.json +98 -0
- package/sdk/api/client.ts +447 -0
- package/sdk/api/datetime.ts +33 -0
- package/sdk/api/index.ts +61 -0
- package/sdk/api/metadata.ts +148 -0
- package/sdk/components/hooks/index.ts +34 -0
- package/sdk/components/hooks/useFilter/index.ts +37 -0
- package/sdk/components/hooks/useFilter/payloadBuilder.utils.ts +298 -0
- package/sdk/components/hooks/useFilter/types.ts +158 -0
- package/sdk/components/hooks/useFilter/useFilter.llm.txt +497 -0
- package/sdk/components/hooks/useFilter/useFilter.ts +494 -0
- package/sdk/components/hooks/useFilter/validation.utils.ts +401 -0
- package/sdk/components/hooks/useForm/apiClient.ts +441 -0
- package/sdk/components/hooks/useForm/expressionValidator.utils.ts +444 -0
- package/sdk/components/hooks/useForm/index.ts +64 -0
- package/sdk/components/hooks/useForm/optimizedExpressionValidator.utils.ts +482 -0
- package/sdk/components/hooks/useForm/ruleClassifier.utils.ts +424 -0
- package/sdk/components/hooks/useForm/schemaParser.utils.ts +519 -0
- package/sdk/components/hooks/useForm/types.ts +630 -0
- package/sdk/components/hooks/useForm/useForm.llm.txt +340 -0
- package/sdk/components/hooks/useForm/useForm.ts +821 -0
- package/sdk/components/hooks/useKanban/apiClient.ts +494 -0
- package/sdk/components/hooks/useKanban/context.ts +14 -0
- package/sdk/components/hooks/useKanban/dragDropManager.ts +529 -0
- package/sdk/components/hooks/useKanban/index.ts +63 -0
- package/sdk/components/hooks/useKanban/types.ts +606 -0
- package/sdk/components/hooks/useKanban/useKanban.llm.txt +482 -0
- package/sdk/components/hooks/useKanban/useKanban.ts +725 -0
- package/sdk/components/hooks/useKanban/useKanbanSimple.ts +389 -0
- package/sdk/components/hooks/useTable/index.ts +5 -0
- package/sdk/components/hooks/useTable/types.ts +154 -0
- package/sdk/components/hooks/useTable/useTable.llm.txt +344 -0
- package/sdk/components/hooks/useTable/useTable.ts +413 -0
- package/sdk/components/index.ts +15 -0
- package/sdk/components/ui/index.ts +2 -0
- package/sdk/components/ui/kanban/Kanban.tsx +134 -0
- package/sdk/components/ui/kanban/index.ts +11 -0
- package/sdk/index.ts +13 -0
- package/sdk/types/base-fields.ts +221 -0
- package/sdk/types/common.ts +306 -0
- package/sdk/types/index.ts +5 -0
- package/sdk/utils/cn.ts +10 -0
- package/sdk/utils/formatting.ts +212 -0
- package/sdk/utils/index.ts +5 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Table hook
|
|
2
|
+
export { useTable } from "./useTable";
|
|
3
|
+
export type {
|
|
4
|
+
UseTableOptions,
|
|
5
|
+
UseTableReturn,
|
|
6
|
+
ColumnDefinition,
|
|
7
|
+
} from "./useTable";
|
|
8
|
+
|
|
9
|
+
// Form hook
|
|
10
|
+
export { useForm } from "./useForm";
|
|
11
|
+
export type {
|
|
12
|
+
UseFormOptions,
|
|
13
|
+
UseFormReturn,
|
|
14
|
+
BackendFieldDefinition,
|
|
15
|
+
ProcessedField,
|
|
16
|
+
ProcessedSchema,
|
|
17
|
+
FormOperation,
|
|
18
|
+
FormMode,
|
|
19
|
+
ValidationResult,
|
|
20
|
+
SubmissionResult,
|
|
21
|
+
} from "./useForm";
|
|
22
|
+
|
|
23
|
+
// Kanban hook
|
|
24
|
+
export { useKanban } from "./useKanban";
|
|
25
|
+
export type {
|
|
26
|
+
UseKanbanOptions,
|
|
27
|
+
UseKanbanReturn,
|
|
28
|
+
KanbanCard,
|
|
29
|
+
KanbanColumn,
|
|
30
|
+
ColumnDefinition as KanbanColumnDefinition,
|
|
31
|
+
} from "./useKanban";
|
|
32
|
+
|
|
33
|
+
// Filter hook
|
|
34
|
+
export * from "./useFilter";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Main hook export
|
|
2
|
+
export { useFilter } from './useFilter';
|
|
3
|
+
|
|
4
|
+
// Type exports
|
|
5
|
+
export type {
|
|
6
|
+
FilterConditionWithId,
|
|
7
|
+
TypedFilterConditionInput,
|
|
8
|
+
FilterState,
|
|
9
|
+
FieldDefinition,
|
|
10
|
+
ValidationResult,
|
|
11
|
+
ValidationError,
|
|
12
|
+
UseFilterOptions,
|
|
13
|
+
UseFilterReturn
|
|
14
|
+
} from './types';
|
|
15
|
+
|
|
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
|
+
// Payload building utilities
|
|
29
|
+
export {
|
|
30
|
+
buildFilterPayload,
|
|
31
|
+
buildFilterPayloadFromState,
|
|
32
|
+
validateFilterPayload,
|
|
33
|
+
cloneFilterPayload,
|
|
34
|
+
mergeFilterPayloads,
|
|
35
|
+
filterPayloadToString,
|
|
36
|
+
areFilterPayloadsEqual
|
|
37
|
+
} from './payloadBuilder.utils';
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import type { Filter, FilterCondition, FilterLogical, LogicalOperator } from "../../../types/common";
|
|
2
|
+
import type { FilterConditionWithId, FilterState } from "../useFilter";
|
|
3
|
+
|
|
4
|
+
// ============================================================
|
|
5
|
+
// PAYLOAD BUILDING UTILITIES
|
|
6
|
+
// ============================================================
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Helper to check if operator is a logical operator
|
|
10
|
+
*/
|
|
11
|
+
const isLogicalOperator = (operator: string): operator is LogicalOperator => {
|
|
12
|
+
return operator === 'And' || operator === 'Or' || operator === 'Not';
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Convert a single FilterConditionWithId to API format (FilterCondition or FilterLogical)
|
|
17
|
+
*/
|
|
18
|
+
const buildCondition = (condition: FilterConditionWithId): FilterCondition | FilterLogical => {
|
|
19
|
+
// Check if this is a logical operator (nested group)
|
|
20
|
+
if (isLogicalOperator(condition.operator)) {
|
|
21
|
+
// Build nested logical filter
|
|
22
|
+
return {
|
|
23
|
+
Operator: condition.operator,
|
|
24
|
+
Condition: (condition.children || [])
|
|
25
|
+
.filter(child => child.isValid)
|
|
26
|
+
.map(child => buildCondition(child))
|
|
27
|
+
};
|
|
28
|
+
} else {
|
|
29
|
+
// Build simple condition
|
|
30
|
+
return {
|
|
31
|
+
Operator: condition.operator,
|
|
32
|
+
LHSField: condition.lhsField!,
|
|
33
|
+
RHSValue: condition.rhsValue,
|
|
34
|
+
RHSType: condition.rhsType || "Constant"
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Build SDK Filter payload from filter state
|
|
41
|
+
* Returns undefined if no valid conditions exist
|
|
42
|
+
* Supports both flat and nested filter structures
|
|
43
|
+
*/
|
|
44
|
+
export const buildFilterPayload = (
|
|
45
|
+
conditions: FilterConditionWithId[],
|
|
46
|
+
logicalOperator: LogicalOperator
|
|
47
|
+
): Filter | undefined => {
|
|
48
|
+
// Return undefined if no conditions
|
|
49
|
+
if (conditions.length === 0) {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Filter out invalid conditions
|
|
54
|
+
const validConditions = conditions.filter(condition => condition.isValid);
|
|
55
|
+
|
|
56
|
+
// Return undefined if no valid conditions
|
|
57
|
+
if (validConditions.length === 0) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Build the filter payload
|
|
62
|
+
return {
|
|
63
|
+
Operator: logicalOperator,
|
|
64
|
+
Condition: validConditions.map(buildCondition)
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Build filter payload from complete filter state
|
|
70
|
+
*/
|
|
71
|
+
export const buildFilterPayloadFromState = (state: FilterState): Filter | undefined => {
|
|
72
|
+
return buildFilterPayload(state.conditions, state.logicalOperator);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Validate that a filter payload is well-formed (supports nested filters)
|
|
77
|
+
*/
|
|
78
|
+
export const validateFilterPayload = (filter: Filter | undefined): boolean => {
|
|
79
|
+
if (!filter) {
|
|
80
|
+
return true; // undefined filter is valid (no filtering)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Check operator
|
|
84
|
+
if (!filter.Operator || !['And', 'Or', 'Not'].includes(filter.Operator)) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Check conditions array
|
|
89
|
+
if (!Array.isArray(filter.Condition) || filter.Condition.length === 0) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Not operator can only have one child
|
|
94
|
+
if (filter.Operator === 'Not' && filter.Condition.length !== 1) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Validate each condition recursively
|
|
99
|
+
return filter.Condition.every(condition => {
|
|
100
|
+
if (!condition.Operator) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Check if this is a logical operator (nested)
|
|
105
|
+
if (isLogicalOperator(condition.Operator)) {
|
|
106
|
+
// Recursively validate nested filter
|
|
107
|
+
return validateFilterPayload(condition as FilterLogical);
|
|
108
|
+
} else {
|
|
109
|
+
// Validate simple condition
|
|
110
|
+
const simpleCondition = condition as FilterCondition;
|
|
111
|
+
return (
|
|
112
|
+
simpleCondition.LHSField !== undefined &&
|
|
113
|
+
simpleCondition.RHSValue !== undefined &&
|
|
114
|
+
(simpleCondition.RHSType === undefined || ['Constant', 'BOField', 'AppVariable'].includes(simpleCondition.RHSType))
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Deep clone a filter condition (supports nested filters)
|
|
122
|
+
*/
|
|
123
|
+
const cloneCondition = (condition: FilterCondition | FilterLogical): FilterCondition | FilterLogical => {
|
|
124
|
+
if (isLogicalOperator(condition.Operator)) {
|
|
125
|
+
// Clone logical filter recursively
|
|
126
|
+
return {
|
|
127
|
+
Operator: condition.Operator,
|
|
128
|
+
Condition: (condition as FilterLogical).Condition.map(cloneCondition)
|
|
129
|
+
};
|
|
130
|
+
} else {
|
|
131
|
+
// Clone simple condition
|
|
132
|
+
const simpleCondition = condition as FilterCondition;
|
|
133
|
+
return {
|
|
134
|
+
Operator: simpleCondition.Operator,
|
|
135
|
+
LHSField: simpleCondition.LHSField,
|
|
136
|
+
RHSValue: simpleCondition.RHSValue,
|
|
137
|
+
RHSType: simpleCondition.RHSType
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Deep clone a filter payload (supports nested filters)
|
|
144
|
+
*/
|
|
145
|
+
export const cloneFilterPayload = (filter: Filter | undefined): Filter | undefined => {
|
|
146
|
+
if (!filter) {
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
Operator: filter.Operator,
|
|
152
|
+
Condition: filter.Condition.map(cloneCondition)
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Merge multiple filter payloads with a logical operator
|
|
158
|
+
*/
|
|
159
|
+
export const mergeFilterPayloads = (
|
|
160
|
+
filters: (Filter | undefined)[],
|
|
161
|
+
operator: LogicalOperator
|
|
162
|
+
): Filter | undefined => {
|
|
163
|
+
const validFilters = filters.filter((filter): filter is Filter =>
|
|
164
|
+
filter !== undefined && validateFilterPayload(filter)
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
if (validFilters.length === 0) {
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (validFilters.length === 1) {
|
|
172
|
+
return cloneFilterPayload(validFilters[0]);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Flatten all conditions
|
|
176
|
+
const allConditions = validFilters.flatMap(filter => filter.Condition);
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
Operator: operator,
|
|
180
|
+
Condition: allConditions
|
|
181
|
+
};
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Convert filter condition to a human-readable string (supports nested filters)
|
|
186
|
+
*/
|
|
187
|
+
const conditionToString = (condition: FilterCondition | FilterLogical): string => {
|
|
188
|
+
if (isLogicalOperator(condition.Operator)) {
|
|
189
|
+
// Handle logical filter
|
|
190
|
+
const logicalFilter = condition as FilterLogical;
|
|
191
|
+
const childStrings = logicalFilter.Condition.map(conditionToString);
|
|
192
|
+
|
|
193
|
+
if (childStrings.length === 1) {
|
|
194
|
+
return condition.Operator === 'Not'
|
|
195
|
+
? `NOT (${childStrings[0]})`
|
|
196
|
+
: childStrings[0];
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return `(${childStrings.join(` ${condition.Operator} `)})`;
|
|
200
|
+
} else {
|
|
201
|
+
// Handle simple condition
|
|
202
|
+
const simpleCondition = condition as FilterCondition;
|
|
203
|
+
const rhsDisplay = Array.isArray(simpleCondition.RHSValue)
|
|
204
|
+
? `[${simpleCondition.RHSValue.join(', ')}]`
|
|
205
|
+
: String(simpleCondition.RHSValue);
|
|
206
|
+
|
|
207
|
+
return `${simpleCondition.LHSField} ${simpleCondition.Operator} ${rhsDisplay}`;
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Convert filter payload to a human-readable string for debugging (supports nested filters)
|
|
213
|
+
*/
|
|
214
|
+
export const filterPayloadToString = (filter: Filter | undefined): string => {
|
|
215
|
+
if (!filter) {
|
|
216
|
+
return "No filters";
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const conditionStrings = filter.Condition.map(conditionToString);
|
|
220
|
+
|
|
221
|
+
if (conditionStrings.length === 1) {
|
|
222
|
+
return conditionStrings[0];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return `(${conditionStrings.join(` ${filter.Operator} `)})`;
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Check if two conditions are equivalent (supports nested filters)
|
|
230
|
+
*/
|
|
231
|
+
const areConditionsEqual = (
|
|
232
|
+
condition1: FilterCondition | FilterLogical,
|
|
233
|
+
condition2: FilterCondition | FilterLogical
|
|
234
|
+
): boolean => {
|
|
235
|
+
// Different operators
|
|
236
|
+
if (condition1.Operator !== condition2.Operator) {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Check if logical operators
|
|
241
|
+
if (isLogicalOperator(condition1.Operator)) {
|
|
242
|
+
const logical1 = condition1 as FilterLogical;
|
|
243
|
+
const logical2 = condition2 as FilterLogical;
|
|
244
|
+
|
|
245
|
+
// Different number of children
|
|
246
|
+
if (logical1.Condition.length !== logical2.Condition.length) {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Recursively compare all children
|
|
251
|
+
return logical1.Condition.every((child1, index) =>
|
|
252
|
+
areConditionsEqual(child1, logical2.Condition[index])
|
|
253
|
+
);
|
|
254
|
+
} else {
|
|
255
|
+
// Compare simple conditions
|
|
256
|
+
const simple1 = condition1 as FilterCondition;
|
|
257
|
+
const simple2 = condition2 as FilterCondition;
|
|
258
|
+
|
|
259
|
+
return (
|
|
260
|
+
simple1.LHSField === simple2.LHSField &&
|
|
261
|
+
JSON.stringify(simple1.RHSValue) === JSON.stringify(simple2.RHSValue) &&
|
|
262
|
+
simple1.RHSType === simple2.RHSType
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Check if two filter payloads are equivalent (supports nested filters)
|
|
269
|
+
*/
|
|
270
|
+
export const areFilterPayloadsEqual = (
|
|
271
|
+
filter1: Filter | undefined,
|
|
272
|
+
filter2: Filter | undefined
|
|
273
|
+
): boolean => {
|
|
274
|
+
// Both undefined
|
|
275
|
+
if (!filter1 && !filter2) {
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// One undefined, one defined
|
|
280
|
+
if (!filter1 || !filter2) {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Different operators
|
|
285
|
+
if (filter1.Operator !== filter2.Operator) {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Different number of conditions
|
|
290
|
+
if (filter1.Condition.length !== filter2.Condition.length) {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Compare each condition recursively
|
|
295
|
+
return filter1.Condition.every((condition1, index) =>
|
|
296
|
+
areConditionsEqual(condition1, filter2.Condition[index])
|
|
297
|
+
);
|
|
298
|
+
};
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import type { Filter, FilterOperator, FilterRHSType, LogicalOperator } from "../../../types/common";
|
|
2
|
+
|
|
3
|
+
// ============================================================
|
|
4
|
+
// TYPE DEFINITIONS
|
|
5
|
+
// ============================================================
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Internal filter condition with ID for management
|
|
9
|
+
* Supports both simple conditions and nested logical groups
|
|
10
|
+
*/
|
|
11
|
+
export interface FilterConditionWithId {
|
|
12
|
+
/** Unique identifier for internal management */
|
|
13
|
+
id: string;
|
|
14
|
+
/** Filter operator (can be condition or logical operator) */
|
|
15
|
+
operator: FilterOperator | LogicalOperator;
|
|
16
|
+
/** Left-hand side field name (required for condition operators) */
|
|
17
|
+
lhsField?: string;
|
|
18
|
+
/** Right-hand side value (required for condition operators) */
|
|
19
|
+
rhsValue?: any;
|
|
20
|
+
/** Right-hand side type (defaults to Constant) */
|
|
21
|
+
rhsType?: FilterRHSType;
|
|
22
|
+
/** Nested conditions (for logical operators: And, Or, Not) */
|
|
23
|
+
children?: FilterConditionWithId[];
|
|
24
|
+
/** Validation state */
|
|
25
|
+
isValid: boolean;
|
|
26
|
+
/** Specific validation errors */
|
|
27
|
+
validationErrors?: string[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Type-safe filter condition input (for addCondition)
|
|
32
|
+
* Constrains lhsField to keys of T for compile-time type checking
|
|
33
|
+
*/
|
|
34
|
+
export interface TypedFilterConditionInput<T> {
|
|
35
|
+
/** Filter operator (can be condition or logical operator) */
|
|
36
|
+
operator: FilterOperator | LogicalOperator;
|
|
37
|
+
/** Left-hand side field name - constrained to keyof T */
|
|
38
|
+
lhsField?: keyof T & string;
|
|
39
|
+
/** Right-hand side value */
|
|
40
|
+
rhsValue?: T[keyof T] | T[keyof T][] | any;
|
|
41
|
+
/** Right-hand side type (defaults to Constant) */
|
|
42
|
+
rhsType?: FilterRHSType;
|
|
43
|
+
/** Nested conditions (for logical operators: And, Or, Not) */
|
|
44
|
+
children?: TypedFilterConditionInput<T>[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface FilterState {
|
|
48
|
+
/** Logical operator for combining conditions */
|
|
49
|
+
logicalOperator: LogicalOperator;
|
|
50
|
+
/** Array of filter conditions with IDs */
|
|
51
|
+
conditions: FilterConditionWithId[];
|
|
52
|
+
}
|
|
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
|
+
export interface ValidationResult {
|
|
68
|
+
/** Whether the validation passed */
|
|
69
|
+
isValid: boolean;
|
|
70
|
+
/** Array of error messages */
|
|
71
|
+
errors: string[];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface ValidationError {
|
|
75
|
+
/** ID of the condition with errors */
|
|
76
|
+
conditionId: string;
|
|
77
|
+
/** Field name */
|
|
78
|
+
field: string;
|
|
79
|
+
/** Error message */
|
|
80
|
+
message: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface UseFilterOptions<T = any> {
|
|
84
|
+
/** Initial filter conditions */
|
|
85
|
+
initialConditions?: FilterConditionWithId[];
|
|
86
|
+
/** Initial logical operator */
|
|
87
|
+
initialLogicalOperator?: LogicalOperator;
|
|
88
|
+
/** Field definitions for validation */
|
|
89
|
+
fieldDefinitions?: Record<keyof T, FieldDefinition>;
|
|
90
|
+
/** Whether to validate conditions on change */
|
|
91
|
+
validateOnChange?: boolean;
|
|
92
|
+
/** Callback when condition is added */
|
|
93
|
+
onConditionAdd?: (condition: FilterConditionWithId) => void;
|
|
94
|
+
/** Callback when condition is updated */
|
|
95
|
+
onConditionUpdate?: (condition: FilterConditionWithId) => void;
|
|
96
|
+
/** Callback when condition is removed */
|
|
97
|
+
onConditionRemove?: (conditionId: string) => void;
|
|
98
|
+
/** Callback when validation errors occur */
|
|
99
|
+
onValidationError?: (errors: ValidationError[]) => void;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface UseFilterReturn<T = any> {
|
|
103
|
+
// Current state
|
|
104
|
+
/** Array of current filter conditions */
|
|
105
|
+
conditions: FilterConditionWithId[];
|
|
106
|
+
/** Current logical operator */
|
|
107
|
+
logicalOperator: LogicalOperator;
|
|
108
|
+
/** SDK-formatted filter payload for API calls */
|
|
109
|
+
filterPayload: Filter | undefined;
|
|
110
|
+
/** Overall validation state */
|
|
111
|
+
isValid: boolean;
|
|
112
|
+
/** Array of validation errors */
|
|
113
|
+
validationErrors: ValidationError[];
|
|
114
|
+
|
|
115
|
+
// Condition management
|
|
116
|
+
/** Add a new filter condition (type-safe: lhsField constrained to keyof T) */
|
|
117
|
+
addCondition: (condition: TypedFilterConditionInput<T>) => string;
|
|
118
|
+
/** Update an existing condition */
|
|
119
|
+
updateCondition: (id: string, updates: Partial<TypedFilterConditionInput<T>>) => boolean;
|
|
120
|
+
/** Remove a condition by ID */
|
|
121
|
+
removeCondition: (id: string) => boolean;
|
|
122
|
+
/** Clear all conditions */
|
|
123
|
+
clearConditions: () => void;
|
|
124
|
+
/** Get a specific condition by ID */
|
|
125
|
+
getCondition: (id: string) => FilterConditionWithId | undefined;
|
|
126
|
+
|
|
127
|
+
// Logical operator management
|
|
128
|
+
/** Set the root logical operator */
|
|
129
|
+
setLogicalOperator: (operator: LogicalOperator) => void;
|
|
130
|
+
|
|
131
|
+
// Bulk operations
|
|
132
|
+
/** Replace all conditions */
|
|
133
|
+
setConditions: (conditions: FilterConditionWithId[]) => void;
|
|
134
|
+
/** Replace a specific condition */
|
|
135
|
+
replaceCondition: (id: string, newCondition: TypedFilterConditionInput<T>) => boolean;
|
|
136
|
+
|
|
137
|
+
// Validation
|
|
138
|
+
/** Validate a single condition */
|
|
139
|
+
validateCondition: (condition: Partial<FilterConditionWithId>) => ValidationResult;
|
|
140
|
+
/** Validate all current conditions */
|
|
141
|
+
validateAllConditions: () => ValidationResult;
|
|
142
|
+
|
|
143
|
+
// State management
|
|
144
|
+
/** Export current filter state */
|
|
145
|
+
exportState: () => FilterState;
|
|
146
|
+
/** Import a filter state */
|
|
147
|
+
importState: (state: FilterState) => void;
|
|
148
|
+
/** Reset to initial state */
|
|
149
|
+
resetToInitial: () => void;
|
|
150
|
+
|
|
151
|
+
// Utilities
|
|
152
|
+
/** Get total number of conditions */
|
|
153
|
+
getConditionCount: () => number;
|
|
154
|
+
/** Whether any conditions exist */
|
|
155
|
+
hasConditions: boolean;
|
|
156
|
+
/** Whether more conditions can be added */
|
|
157
|
+
canAddCondition: boolean;
|
|
158
|
+
}
|