@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,444 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// EXPRESSION TREE VALIDATOR
|
|
3
|
+
// ============================================================
|
|
4
|
+
// Evaluates backend expression trees for form validation
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
ExpressionTree,
|
|
8
|
+
EvaluationContext,
|
|
9
|
+
ValidationResult,
|
|
10
|
+
ValidationRule,
|
|
11
|
+
} from "./types";
|
|
12
|
+
|
|
13
|
+
// ============================================================
|
|
14
|
+
// SYSTEM VALUES
|
|
15
|
+
// ============================================================
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get current system values for expression evaluation
|
|
19
|
+
*/
|
|
20
|
+
function getSystemValues(): Record<string, any> {
|
|
21
|
+
const now = new Date();
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
NOW: now,
|
|
25
|
+
TODAY: new Date(now.getFullYear(), now.getMonth(), now.getDate()),
|
|
26
|
+
CURRENT_USER: {
|
|
27
|
+
// These would typically come from auth context
|
|
28
|
+
EmpId: 1,
|
|
29
|
+
Email: "user@example.com",
|
|
30
|
+
FirstName: "John",
|
|
31
|
+
LastName: "Doe",
|
|
32
|
+
Role: "User",
|
|
33
|
+
},
|
|
34
|
+
CURRENT_USER_ID: 1,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ============================================================
|
|
39
|
+
// EXPRESSION FUNCTIONS
|
|
40
|
+
// ============================================================
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Built-in functions for expression evaluation
|
|
44
|
+
*/
|
|
45
|
+
const FUNCTIONS = {
|
|
46
|
+
// String functions
|
|
47
|
+
CONCAT: (...args: any[]) => args.map((arg) => String(arg || "")).join(""),
|
|
48
|
+
TRIM: (str: string) => String(str || "").trim(),
|
|
49
|
+
LENGTH: (str: string) => String(str || "").length,
|
|
50
|
+
UPPER: (str: string) => String(str || "").toUpperCase(),
|
|
51
|
+
LOWER: (str: string) => String(str || "").toLowerCase(),
|
|
52
|
+
SUBSTRING: (str: string, start: number, length?: number) => {
|
|
53
|
+
const s = String(str || "");
|
|
54
|
+
return length !== undefined
|
|
55
|
+
? s.substring(start, start + length)
|
|
56
|
+
: s.substring(start);
|
|
57
|
+
},
|
|
58
|
+
CONTAINS: (str: string, search: string) => {
|
|
59
|
+
return String(str || "").includes(String(search || ""));
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
// Date functions
|
|
63
|
+
YEAR: (date: Date) => new Date(date).getFullYear(),
|
|
64
|
+
MONTH: (date: Date) => new Date(date).getMonth() + 1, // 1-based
|
|
65
|
+
DAY: (date: Date) => new Date(date).getDate(),
|
|
66
|
+
DATE_DIFF: (date1: Date, date2: Date) => {
|
|
67
|
+
const d1 = new Date(date1);
|
|
68
|
+
const d2 = new Date(date2);
|
|
69
|
+
const diffTime = Math.abs(d1.getTime() - d2.getTime());
|
|
70
|
+
return Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
|
71
|
+
},
|
|
72
|
+
ADD_DAYS: (date: Date, days: number) => {
|
|
73
|
+
const result = new Date(date);
|
|
74
|
+
result.setDate(result.getDate() + days);
|
|
75
|
+
return result;
|
|
76
|
+
},
|
|
77
|
+
ADD_MONTHS: (date: Date, months: number) => {
|
|
78
|
+
const result = new Date(date);
|
|
79
|
+
result.setMonth(result.getMonth() + months);
|
|
80
|
+
return result;
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
// Math functions
|
|
84
|
+
SUM: (...args: number[]) =>
|
|
85
|
+
args.reduce((sum, val) => sum + (Number(val) || 0), 0),
|
|
86
|
+
AVG: (...args: number[]) => {
|
|
87
|
+
const nums = args.filter((val) => !isNaN(Number(val)));
|
|
88
|
+
return nums.length > 0
|
|
89
|
+
? nums.reduce((sum, val) => sum + Number(val), 0) / nums.length
|
|
90
|
+
: 0;
|
|
91
|
+
},
|
|
92
|
+
MIN: (...args: number[]) => Math.min(...args.map((val) => Number(val) || 0)),
|
|
93
|
+
MAX: (...args: number[]) => Math.max(...args.map((val) => Number(val) || 0)),
|
|
94
|
+
ROUND: (num: number) => Math.round(Number(num) || 0),
|
|
95
|
+
FLOOR: (num: number) => Math.floor(Number(num) || 0),
|
|
96
|
+
CEIL: (num: number) => Math.ceil(Number(num) || 0),
|
|
97
|
+
ABS: (num: number) => Math.abs(Number(num) || 0),
|
|
98
|
+
|
|
99
|
+
// Conditional functions
|
|
100
|
+
IF: (condition: any, trueValue: any, falseValue: any) =>
|
|
101
|
+
condition ? trueValue : falseValue,
|
|
102
|
+
|
|
103
|
+
// Validation functions
|
|
104
|
+
IS_NULL: (value: any) => value === null || value === undefined,
|
|
105
|
+
IS_EMPTY: (value: string) => !value || String(value).trim() === "",
|
|
106
|
+
IS_NUMBER: (value: any) =>
|
|
107
|
+
!isNaN(Number(value)) &&
|
|
108
|
+
value !== "" &&
|
|
109
|
+
value !== null &&
|
|
110
|
+
value !== undefined,
|
|
111
|
+
IS_DATE: (value: any) => {
|
|
112
|
+
const date = new Date(value);
|
|
113
|
+
return !isNaN(date.getTime());
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
// System functions
|
|
117
|
+
AUTO_NUMBER: () => Math.floor(Math.random() * 10000) + 1000, // Mock implementation
|
|
118
|
+
UUID: () =>
|
|
119
|
+
"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
120
|
+
const r = (Math.random() * 16) | 0;
|
|
121
|
+
const v = c == "x" ? r : (r & 0x3) | 0x8;
|
|
122
|
+
return v.toString(16);
|
|
123
|
+
}),
|
|
124
|
+
|
|
125
|
+
// Array functions
|
|
126
|
+
ARRAY_LENGTH: (arr: any[]) => (Array.isArray(arr) ? arr.length : 0),
|
|
127
|
+
ARRAY_CONTAINS: (arr: any[], value: any) =>
|
|
128
|
+
Array.isArray(arr) ? arr.includes(value) : false,
|
|
129
|
+
ARRAY_JOIN: (arr: any[], separator: string = ",") =>
|
|
130
|
+
Array.isArray(arr) ? arr.join(separator) : "",
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// ============================================================
|
|
134
|
+
// EXPRESSION EVALUATOR
|
|
135
|
+
// ============================================================
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Evaluate an expression tree node
|
|
139
|
+
*/
|
|
140
|
+
function evaluateNode(node: ExpressionTree, context: EvaluationContext): any {
|
|
141
|
+
switch (node.Type) {
|
|
142
|
+
case "Literal":
|
|
143
|
+
return node.Value;
|
|
144
|
+
|
|
145
|
+
case "SystemIdentifier":
|
|
146
|
+
if (node.Property) {
|
|
147
|
+
const systemValue = context.systemValues[node.Name!];
|
|
148
|
+
return getNestedValue(systemValue, node.Property.Name);
|
|
149
|
+
}
|
|
150
|
+
return context.systemValues[node.Name!];
|
|
151
|
+
|
|
152
|
+
case "Identifier":
|
|
153
|
+
if (node.Property) {
|
|
154
|
+
const value = getIdentifierValue(node, context);
|
|
155
|
+
return getNestedValue(value, node.Property.Name);
|
|
156
|
+
}
|
|
157
|
+
return getIdentifierValue(node, context);
|
|
158
|
+
|
|
159
|
+
case "MemberExpression":
|
|
160
|
+
if (!node.Arguments || node.Arguments.length === 0) {
|
|
161
|
+
throw new Error("MemberExpression requires Arguments array");
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const object = evaluateNode(node.Arguments[0], context);
|
|
165
|
+
const propertyName = node.Arguments[0].Property?.Name;
|
|
166
|
+
|
|
167
|
+
if (propertyName) {
|
|
168
|
+
return getNestedValue(object, propertyName);
|
|
169
|
+
}
|
|
170
|
+
return object;
|
|
171
|
+
|
|
172
|
+
case "BinaryExpression":
|
|
173
|
+
if (!node.Arguments || node.Arguments.length !== 2) {
|
|
174
|
+
throw new Error("BinaryExpression requires exactly 2 arguments");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const left = evaluateNode(node.Arguments[0], context);
|
|
178
|
+
const right = evaluateNode(node.Arguments[1], context);
|
|
179
|
+
|
|
180
|
+
return evaluateBinaryOperation(node.Operator!, left, right);
|
|
181
|
+
|
|
182
|
+
case "LogicalExpression":
|
|
183
|
+
if (!node.Arguments || node.Arguments.length < 2) {
|
|
184
|
+
throw new Error("LogicalExpression requires at least 2 arguments");
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return evaluateLogicalOperation(node.Operator!, node.Arguments, context);
|
|
188
|
+
|
|
189
|
+
case "CallExpression":
|
|
190
|
+
if (!node.Callee) {
|
|
191
|
+
throw new Error("CallExpression requires Callee");
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const func = FUNCTIONS[node.Callee as keyof typeof FUNCTIONS];
|
|
195
|
+
if (!func) {
|
|
196
|
+
throw new Error(`Unknown function: ${node.Callee}`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const args = (node.Arguments || []).map((arg) =>
|
|
200
|
+
evaluateNode(arg, context)
|
|
201
|
+
);
|
|
202
|
+
return (func as any)(...args);
|
|
203
|
+
|
|
204
|
+
case "AssignmentExpression":
|
|
205
|
+
if (!node.Arguments || node.Arguments.length !== 1) {
|
|
206
|
+
throw new Error("AssignmentExpression requires exactly 1 argument");
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return evaluateNode(node.Arguments[0], context);
|
|
210
|
+
|
|
211
|
+
default:
|
|
212
|
+
throw new Error(`Unknown expression type: ${node.Type}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Get value from identifier based on source
|
|
218
|
+
*/
|
|
219
|
+
function getIdentifierValue(
|
|
220
|
+
node: ExpressionTree,
|
|
221
|
+
context: EvaluationContext
|
|
222
|
+
): any {
|
|
223
|
+
const { Name, Source } = node;
|
|
224
|
+
|
|
225
|
+
if (!Name) {
|
|
226
|
+
throw new Error("Identifier requires Name");
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
switch (Source) {
|
|
230
|
+
case "Input":
|
|
231
|
+
default:
|
|
232
|
+
// Default to form values for field references
|
|
233
|
+
return context.formValues[Name];
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Get nested property value
|
|
239
|
+
*/
|
|
240
|
+
function getNestedValue(obj: any, propertyPath: string): any {
|
|
241
|
+
if (!obj || typeof obj !== "object") {
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return obj[propertyPath];
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Evaluate binary operation
|
|
250
|
+
*/
|
|
251
|
+
function evaluateBinaryOperation(operator: string, left: any, right: any): any {
|
|
252
|
+
// Helper to extract numeric value from currency objects or other values
|
|
253
|
+
const toNumber = (val: any): number => {
|
|
254
|
+
if (val === null || val === undefined) return 0;
|
|
255
|
+
if (typeof val === "number") return val;
|
|
256
|
+
if (typeof val === "object" && val !== null && "value" in val) {
|
|
257
|
+
// Handle currency objects like {value: 100, currency: "USD"}
|
|
258
|
+
return Number(val.value) || 0;
|
|
259
|
+
}
|
|
260
|
+
return Number(val) || 0;
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
switch (operator) {
|
|
264
|
+
case "==":
|
|
265
|
+
return left == right;
|
|
266
|
+
case "!=":
|
|
267
|
+
return left != right;
|
|
268
|
+
case ">":
|
|
269
|
+
return toNumber(left) > toNumber(right);
|
|
270
|
+
case "<":
|
|
271
|
+
return toNumber(left) < toNumber(right);
|
|
272
|
+
case ">=":
|
|
273
|
+
return toNumber(left) >= toNumber(right);
|
|
274
|
+
case "<=":
|
|
275
|
+
return toNumber(left) <= toNumber(right);
|
|
276
|
+
case "+":
|
|
277
|
+
return toNumber(left) + toNumber(right);
|
|
278
|
+
case "-":
|
|
279
|
+
return toNumber(left) - toNumber(right);
|
|
280
|
+
case "*":
|
|
281
|
+
return toNumber(left) * toNumber(right);
|
|
282
|
+
case "/":
|
|
283
|
+
const divisor = toNumber(right);
|
|
284
|
+
return divisor !== 0 ? toNumber(left) / divisor : 0;
|
|
285
|
+
default:
|
|
286
|
+
throw new Error(`Unknown binary operator: ${operator}`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Evaluate logical operation
|
|
292
|
+
*/
|
|
293
|
+
function evaluateLogicalOperation(
|
|
294
|
+
operator: string,
|
|
295
|
+
args: ExpressionTree[],
|
|
296
|
+
context: EvaluationContext
|
|
297
|
+
): boolean {
|
|
298
|
+
switch (operator) {
|
|
299
|
+
case "AND":
|
|
300
|
+
return args.every((arg) => Boolean(evaluateNode(arg, context)));
|
|
301
|
+
case "OR":
|
|
302
|
+
return args.some((arg) => Boolean(evaluateNode(arg, context)));
|
|
303
|
+
default:
|
|
304
|
+
throw new Error(`Unknown logical operator: ${operator}`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// ============================================================
|
|
309
|
+
// PUBLIC API
|
|
310
|
+
// ============================================================
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Evaluate a complete expression tree
|
|
314
|
+
*/
|
|
315
|
+
export function evaluateExpression(
|
|
316
|
+
expressionTree: ExpressionTree,
|
|
317
|
+
formValues: Record<string, any>,
|
|
318
|
+
referenceData: Record<string, any> = {}
|
|
319
|
+
): any {
|
|
320
|
+
const context: EvaluationContext = {
|
|
321
|
+
formValues,
|
|
322
|
+
systemValues: getSystemValues(),
|
|
323
|
+
referenceData,
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
try {
|
|
327
|
+
return evaluateNode(expressionTree, context);
|
|
328
|
+
} catch (error) {
|
|
329
|
+
console.warn("Expression evaluation failed:", error);
|
|
330
|
+
return false; // Default to false for validation expressions
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Validate a field using backend validation rules
|
|
336
|
+
*/
|
|
337
|
+
export function validateField<T = Record<string, any>>(
|
|
338
|
+
fieldName: string,
|
|
339
|
+
fieldValue: any,
|
|
340
|
+
validationRules: ValidationRule[],
|
|
341
|
+
formValues: T,
|
|
342
|
+
referenceData: Record<string, any> = {}
|
|
343
|
+
): ValidationResult<T> {
|
|
344
|
+
// If no validation rules, field is valid
|
|
345
|
+
if (!validationRules || validationRules.length === 0) {
|
|
346
|
+
return { isValid: true };
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Set current field value in form values
|
|
350
|
+
const currentFormValues = { ...formValues, [fieldName]: fieldValue };
|
|
351
|
+
|
|
352
|
+
// Check each validation rule
|
|
353
|
+
for (const rule of validationRules) {
|
|
354
|
+
try {
|
|
355
|
+
const isValid = evaluateExpression(
|
|
356
|
+
rule.ExpressionTree,
|
|
357
|
+
currentFormValues,
|
|
358
|
+
referenceData
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
if (!isValid) {
|
|
362
|
+
return {
|
|
363
|
+
isValid: false,
|
|
364
|
+
message: rule.Message || `Validation failed for ${rule.Name}`,
|
|
365
|
+
fieldName: fieldName as keyof T,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
} catch (error) {
|
|
369
|
+
console.warn(`Validation rule ${rule.Id} failed to evaluate:`, error);
|
|
370
|
+
// Continue with other rules if one fails
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return { isValid: true };
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Validate all cross-field validation rules
|
|
379
|
+
*/
|
|
380
|
+
export function validateCrossField<T = Record<string, any>>(
|
|
381
|
+
validationRules: Array<{
|
|
382
|
+
Id: string;
|
|
383
|
+
Condition: { ExpressionTree: ExpressionTree };
|
|
384
|
+
Message: string;
|
|
385
|
+
}>,
|
|
386
|
+
formValues: T,
|
|
387
|
+
referenceData: Record<string, any> = {}
|
|
388
|
+
): ValidationResult<T>[] {
|
|
389
|
+
const results: ValidationResult<T>[] = [];
|
|
390
|
+
|
|
391
|
+
for (const rule of validationRules) {
|
|
392
|
+
try {
|
|
393
|
+
const isValid = evaluateExpression(
|
|
394
|
+
rule.Condition.ExpressionTree,
|
|
395
|
+
formValues as Record<string, any>,
|
|
396
|
+
referenceData
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
if (!isValid) {
|
|
400
|
+
results.push({
|
|
401
|
+
isValid: false,
|
|
402
|
+
message: rule.Message,
|
|
403
|
+
fieldName: rule.Id as keyof T,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
} catch (error) {
|
|
407
|
+
console.warn(`Cross-field validation rule ${rule.Id} failed:`, error);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return results;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Calculate computed field value
|
|
416
|
+
*/
|
|
417
|
+
export function calculateComputedValue(
|
|
418
|
+
expressionTree: ExpressionTree,
|
|
419
|
+
formValues: Record<string, any>,
|
|
420
|
+
referenceData: Record<string, any> = {}
|
|
421
|
+
): any {
|
|
422
|
+
try {
|
|
423
|
+
return evaluateExpression(expressionTree, formValues, referenceData);
|
|
424
|
+
} catch (error) {
|
|
425
|
+
console.warn("Computed field calculation failed:", error);
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Calculate default field value
|
|
432
|
+
*/
|
|
433
|
+
export function calculateDefaultValue(
|
|
434
|
+
expressionTree: ExpressionTree,
|
|
435
|
+
formValues: Record<string, any> = {},
|
|
436
|
+
referenceData: Record<string, any> = {}
|
|
437
|
+
): any {
|
|
438
|
+
try {
|
|
439
|
+
return evaluateExpression(expressionTree, formValues, referenceData);
|
|
440
|
+
} catch (error) {
|
|
441
|
+
console.warn("Default value calculation failed:", error);
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// USE FORM HOOK - MAIN EXPORT
|
|
3
|
+
// ============================================================
|
|
4
|
+
|
|
5
|
+
// Main hook
|
|
6
|
+
export { useForm } from './useForm';
|
|
7
|
+
|
|
8
|
+
// Types
|
|
9
|
+
export type {
|
|
10
|
+
UseFormOptions,
|
|
11
|
+
UseFormReturn,
|
|
12
|
+
BackendSchema,
|
|
13
|
+
BackendFieldDefinition,
|
|
14
|
+
ProcessedField,
|
|
15
|
+
ProcessedSchema,
|
|
16
|
+
FormOperation,
|
|
17
|
+
FormMode,
|
|
18
|
+
ValidationResult,
|
|
19
|
+
SubmissionResult,
|
|
20
|
+
ExpressionTree,
|
|
21
|
+
ValidationRule,
|
|
22
|
+
Formula,
|
|
23
|
+
DefaultValue,
|
|
24
|
+
ReferenceField,
|
|
25
|
+
FieldValues,
|
|
26
|
+
EvaluationContext
|
|
27
|
+
} from './types';
|
|
28
|
+
|
|
29
|
+
// Utilities
|
|
30
|
+
export {
|
|
31
|
+
processSchema,
|
|
32
|
+
updateComputedFields,
|
|
33
|
+
buildDependencyMap,
|
|
34
|
+
extractReferenceFields,
|
|
35
|
+
validateSchema,
|
|
36
|
+
buildReferenceFieldConfig
|
|
37
|
+
} from './schemaParser.utils';
|
|
38
|
+
|
|
39
|
+
export {
|
|
40
|
+
evaluateExpression,
|
|
41
|
+
validateField,
|
|
42
|
+
validateCrossField,
|
|
43
|
+
calculateComputedValue,
|
|
44
|
+
calculateDefaultValue
|
|
45
|
+
} from './expressionValidator.utils';
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
fetchFormSchema,
|
|
49
|
+
fetchFormSchemaWithRetry,
|
|
50
|
+
fetchRecord,
|
|
51
|
+
submitFormData,
|
|
52
|
+
fetchReferenceData,
|
|
53
|
+
fetchAllReferenceData,
|
|
54
|
+
validateFormData,
|
|
55
|
+
cleanFormData,
|
|
56
|
+
parseApiError,
|
|
57
|
+
isNetworkError,
|
|
58
|
+
isValidationError,
|
|
59
|
+
setCacheData,
|
|
60
|
+
getCacheData,
|
|
61
|
+
clearCache,
|
|
62
|
+
fetchFormSchemaWithCache,
|
|
63
|
+
fetchReferenceDataWithCache
|
|
64
|
+
} from './apiClient';
|