@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,424 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// RULE CLASSIFIER
|
|
3
|
+
// ============================================================
|
|
4
|
+
// Classifies rules by type and determines execution strategy
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
BDOSchema,
|
|
8
|
+
BackendSchema,
|
|
9
|
+
ProcessedSchema,
|
|
10
|
+
ValidationRule,
|
|
11
|
+
RuleType,
|
|
12
|
+
FieldPermission,
|
|
13
|
+
} from "./types";
|
|
14
|
+
|
|
15
|
+
// ============================================================
|
|
16
|
+
// SCHEMA NORMALIZATION
|
|
17
|
+
// ============================================================
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Check if a validation rule represents a "required" validation
|
|
21
|
+
* Detects patterns like: field != null, TRIM(field) != '', etc.
|
|
22
|
+
*/
|
|
23
|
+
function isRequiredValidationRule(rule: ValidationRule, fieldName: string): boolean {
|
|
24
|
+
const expression = rule.Expression?.toLowerCase() || '';
|
|
25
|
+
const ruleName = rule.Name?.toLowerCase() || '';
|
|
26
|
+
const ruleId = rule.Id?.toLowerCase() || '';
|
|
27
|
+
const fieldLower = fieldName.toLowerCase();
|
|
28
|
+
|
|
29
|
+
// Check if rule name/ID indicates it's a required rule
|
|
30
|
+
if (ruleName.includes('required') || ruleId.includes('required')) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Check expression patterns for required validation
|
|
35
|
+
// Patterns: "field != null", "field != ''" , "TRIM(field) != ''"
|
|
36
|
+
const requiredPatterns = [
|
|
37
|
+
`${fieldLower} != null`,
|
|
38
|
+
`${fieldLower} != ''`,
|
|
39
|
+
`trim(${fieldLower}) != ''`,
|
|
40
|
+
`${fieldLower}!=null`,
|
|
41
|
+
`${fieldLower}!=''`,
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
return requiredPatterns.some(pattern => expression.includes(pattern));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Normalize BDO schema to ensure validation rules are in centralized format
|
|
49
|
+
* Extracts inline validation rules from fields and adds them to Rules.Validation
|
|
50
|
+
*/
|
|
51
|
+
export function normalizeBDOSchema(schema: BDOSchema): BDOSchema {
|
|
52
|
+
const normalizedSchema = { ...schema };
|
|
53
|
+
|
|
54
|
+
// Initialize Rules section if it doesn't exist
|
|
55
|
+
if (!normalizedSchema.Rules) {
|
|
56
|
+
normalizedSchema.Rules = {
|
|
57
|
+
Validation: {},
|
|
58
|
+
Computation: {},
|
|
59
|
+
BusinessLogic: {},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!normalizedSchema.Rules.Validation) {
|
|
64
|
+
normalizedSchema.Rules.Validation = {};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!normalizedSchema.Rules.Computation) {
|
|
68
|
+
normalizedSchema.Rules.Computation = {};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!normalizedSchema.Rules.BusinessLogic) {
|
|
72
|
+
normalizedSchema.Rules.BusinessLogic = {};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Normalize fields
|
|
76
|
+
const normalizedFields = { ...normalizedSchema.Fields };
|
|
77
|
+
|
|
78
|
+
Object.entries(normalizedFields).forEach(([fieldName, field]) => {
|
|
79
|
+
let isRequired = field.Required || false;
|
|
80
|
+
|
|
81
|
+
if (field.Validation && Array.isArray(field.Validation)) {
|
|
82
|
+
// Check if validation contains inline rule objects
|
|
83
|
+
if (field.Validation.length > 0 && typeof field.Validation[0] === 'object') {
|
|
84
|
+
// Extract inline validation rules
|
|
85
|
+
const ruleIds: string[] = [];
|
|
86
|
+
|
|
87
|
+
(field.Validation as ValidationRule[]).forEach((rule) => {
|
|
88
|
+
// Add rule to centralized Rules.Validation
|
|
89
|
+
normalizedSchema.Rules.Validation![rule.Id] = rule;
|
|
90
|
+
ruleIds.push(rule.Id);
|
|
91
|
+
|
|
92
|
+
// Check if this is a required validation rule
|
|
93
|
+
if (isRequiredValidationRule(rule, fieldName)) {
|
|
94
|
+
isRequired = true;
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Replace inline rules with rule IDs and set Required flag
|
|
99
|
+
normalizedFields[fieldName] = {
|
|
100
|
+
...field,
|
|
101
|
+
Validation: ruleIds,
|
|
102
|
+
Required: isRequired,
|
|
103
|
+
};
|
|
104
|
+
} else {
|
|
105
|
+
// If validation contains strings (rule IDs), check if any are required rules
|
|
106
|
+
const validationRuleIds = field.Validation as string[];
|
|
107
|
+
validationRuleIds.forEach((ruleId) => {
|
|
108
|
+
const rule = normalizedSchema.Rules.Validation?.[ruleId];
|
|
109
|
+
if (rule && isRequiredValidationRule(rule, fieldName)) {
|
|
110
|
+
isRequired = true;
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
if (isRequired && !field.Required) {
|
|
115
|
+
normalizedFields[fieldName] = {
|
|
116
|
+
...field,
|
|
117
|
+
Required: true,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Also extract Formula as a Computation rule if it exists
|
|
124
|
+
if (field.Formula) {
|
|
125
|
+
const computationRuleId = `RULE_COMPUTE_${fieldName.toUpperCase()}`;
|
|
126
|
+
normalizedSchema.Rules.Computation![computationRuleId] = {
|
|
127
|
+
Id: computationRuleId,
|
|
128
|
+
Name: field.Formula.Id || `Compute ${fieldName}`,
|
|
129
|
+
Description: field.Formula.Description || `Computes value for ${fieldName}`,
|
|
130
|
+
Expression: field.Formula.Expression,
|
|
131
|
+
ExpressionTree: field.Formula.ExpressionTree,
|
|
132
|
+
ResultType: field.Type,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
normalizedSchema.Fields = normalizedFields;
|
|
138
|
+
|
|
139
|
+
return normalizedSchema;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ============================================================
|
|
143
|
+
// RULE CLASSIFICATION
|
|
144
|
+
// ============================================================
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Classify rules by type from BDO schema
|
|
148
|
+
*/
|
|
149
|
+
export function classifyRules(schema: BDOSchema): ProcessedSchema["rules"] {
|
|
150
|
+
const rules = {
|
|
151
|
+
validation: {} as Record<string, ValidationRule>,
|
|
152
|
+
computation: {} as Record<string, ValidationRule>,
|
|
153
|
+
businessLogic: {} as Record<string, ValidationRule>,
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// Extract rules from BDO Rules section
|
|
157
|
+
if (schema.Rules?.Validation) {
|
|
158
|
+
rules.validation = { ...schema.Rules.Validation };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (schema.Rules?.Computation) {
|
|
162
|
+
rules.computation = { ...schema.Rules.Computation };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (schema.Rules?.BusinessLogic) {
|
|
166
|
+
rules.businessLogic = { ...schema.Rules.BusinessLogic };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return rules;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Create field-to-rule mapping
|
|
174
|
+
*/
|
|
175
|
+
/**
|
|
176
|
+
* Infer which field each computation rule targets
|
|
177
|
+
* Strategy: Match rule to fields marked as Computed: true
|
|
178
|
+
* by analyzing the rule's target field from its name/description
|
|
179
|
+
*/
|
|
180
|
+
function inferComputationRuleTargets(
|
|
181
|
+
schema: BDOSchema
|
|
182
|
+
): Record<string, string> {
|
|
183
|
+
const ruleToField: Record<string, string> = {};
|
|
184
|
+
|
|
185
|
+
if (!schema.Rules?.Computation) return ruleToField;
|
|
186
|
+
|
|
187
|
+
// Find all computed fields (fields with Computed: true)
|
|
188
|
+
const computedFieldNames = Object.entries(schema.Fields)
|
|
189
|
+
.filter(([_, field]) => field.Computed)
|
|
190
|
+
.map(([fieldName]) => fieldName);
|
|
191
|
+
|
|
192
|
+
// Sort by length descending to match longer names first (e.g., LowStock before Stock)
|
|
193
|
+
computedFieldNames.sort((a, b) => b.length - a.length);
|
|
194
|
+
|
|
195
|
+
Object.entries(schema.Rules.Computation).forEach(([ruleId, rule]) => {
|
|
196
|
+
// Strategy 1: Match rule ID to computed field names
|
|
197
|
+
// Normalize both to handle camelCase vs SNAKE_CASE
|
|
198
|
+
const normalizedRuleId = ruleId.toUpperCase().replace(/[_-]/g, "");
|
|
199
|
+
|
|
200
|
+
for (const fieldName of computedFieldNames) {
|
|
201
|
+
const normalizedFieldName = fieldName.toUpperCase().replace(/[_-]/g, "");
|
|
202
|
+
|
|
203
|
+
if (normalizedRuleId.includes(normalizedFieldName)) {
|
|
204
|
+
ruleToField[ruleId] = fieldName;
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Strategy 2: Check rule name/description
|
|
210
|
+
const searchText =
|
|
211
|
+
`${rule.Name || ""} ${rule.Description || ""}`.toLowerCase();
|
|
212
|
+
|
|
213
|
+
for (const fieldName of computedFieldNames) {
|
|
214
|
+
if (searchText.includes(fieldName.toLowerCase())) {
|
|
215
|
+
ruleToField[ruleId] = fieldName;
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
return ruleToField;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function createFieldRuleMapping(
|
|
225
|
+
schema: BDOSchema,
|
|
226
|
+
classifiedRules: ProcessedSchema["rules"]
|
|
227
|
+
): ProcessedSchema["fieldRules"] {
|
|
228
|
+
const fieldRules: ProcessedSchema["fieldRules"] = {};
|
|
229
|
+
|
|
230
|
+
// Initialize all fields
|
|
231
|
+
Object.keys(schema.Fields).forEach((fieldName) => {
|
|
232
|
+
fieldRules[fieldName] = {
|
|
233
|
+
validation: [],
|
|
234
|
+
computation: [],
|
|
235
|
+
businessLogic: [],
|
|
236
|
+
};
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// Map validation rules (from field.Validation array)
|
|
240
|
+
// After normalization, field.Validation is always string[]
|
|
241
|
+
Object.entries(schema.Fields).forEach(([fieldName, field]) => {
|
|
242
|
+
if (field.Validation && Array.isArray(field.Validation)) {
|
|
243
|
+
// Type assertion: after normalization, Validation is always string[]
|
|
244
|
+
const validationRuleIds = field.Validation as string[];
|
|
245
|
+
validationRuleIds.forEach((ruleId) => {
|
|
246
|
+
if (classifiedRules.validation[ruleId]) {
|
|
247
|
+
fieldRules[fieldName].validation.push(ruleId);
|
|
248
|
+
} else if (classifiedRules.computation[ruleId]) {
|
|
249
|
+
// Some computation rules might be in Validation array
|
|
250
|
+
fieldRules[fieldName].computation.push(ruleId);
|
|
251
|
+
} else if (classifiedRules.businessLogic[ruleId]) {
|
|
252
|
+
fieldRules[fieldName].businessLogic.push(ruleId);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Add computation rule if field has formula (legacy support)
|
|
258
|
+
if (field.Formula) {
|
|
259
|
+
Object.entries(classifiedRules.computation).forEach(([ruleId, rule]) => {
|
|
260
|
+
if (rule.Expression === field.Formula?.Expression) {
|
|
261
|
+
if (!fieldRules[fieldName].computation.includes(ruleId)) {
|
|
262
|
+
fieldRules[fieldName].computation.push(ruleId);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// Map computation rules (infer from rule ID or expression)
|
|
270
|
+
const ruleTargets = inferComputationRuleTargets(schema);
|
|
271
|
+
Object.entries(ruleTargets).forEach(([ruleId, fieldName]) => {
|
|
272
|
+
if (
|
|
273
|
+
fieldRules[fieldName] &&
|
|
274
|
+
!fieldRules[fieldName].computation.includes(ruleId)
|
|
275
|
+
) {
|
|
276
|
+
fieldRules[fieldName].computation.push(ruleId);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
return fieldRules;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// ============================================================
|
|
284
|
+
// PERMISSION CALCULATION
|
|
285
|
+
// ============================================================
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Calculate field permissions based on user role
|
|
289
|
+
*/
|
|
290
|
+
export function calculateFieldPermissions(
|
|
291
|
+
schema: BDOSchema,
|
|
292
|
+
userRole?: string
|
|
293
|
+
): Record<string, FieldPermission> {
|
|
294
|
+
const fieldPermissions: Record<string, FieldPermission> = {};
|
|
295
|
+
|
|
296
|
+
// Default permissions (no role specified)
|
|
297
|
+
const defaultPermission: FieldPermission = {
|
|
298
|
+
editable: true,
|
|
299
|
+
readable: true,
|
|
300
|
+
hidden: false,
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
Object.keys(schema.Fields).forEach((fieldName) => {
|
|
304
|
+
if (!userRole || !schema.RolePermission?.[userRole]) {
|
|
305
|
+
fieldPermissions[fieldName] = defaultPermission;
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const rolePermission = schema.RolePermission[userRole];
|
|
310
|
+
|
|
311
|
+
// Check if field is in editable list
|
|
312
|
+
const isEditable =
|
|
313
|
+
rolePermission.Editable?.includes(fieldName) ||
|
|
314
|
+
rolePermission.Editable?.includes("*") ||
|
|
315
|
+
false;
|
|
316
|
+
|
|
317
|
+
// Check if field is in readonly list
|
|
318
|
+
const isReadable =
|
|
319
|
+
rolePermission.ReadOnly?.includes(fieldName) ||
|
|
320
|
+
rolePermission.ReadOnly?.includes("*") ||
|
|
321
|
+
isEditable; // Editable fields are also readable
|
|
322
|
+
|
|
323
|
+
fieldPermissions[fieldName] = {
|
|
324
|
+
editable: isEditable && !rolePermission.ReadOnly?.includes(fieldName),
|
|
325
|
+
readable: isReadable,
|
|
326
|
+
hidden: !isReadable,
|
|
327
|
+
};
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
return fieldPermissions;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// ============================================================
|
|
334
|
+
// RULE EXECUTION STRATEGY
|
|
335
|
+
// ============================================================
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Determine if rule should execute client-side or server-side
|
|
339
|
+
*/
|
|
340
|
+
export function getRuleExecutionStrategy(
|
|
341
|
+
ruleType: RuleType
|
|
342
|
+
): "client" | "server" {
|
|
343
|
+
switch (ruleType) {
|
|
344
|
+
case "Validation":
|
|
345
|
+
return "client"; // Always execute validation on client
|
|
346
|
+
case "Computation":
|
|
347
|
+
case "BusinessLogic":
|
|
348
|
+
return "server"; // Execute computation and business logic on server
|
|
349
|
+
default:
|
|
350
|
+
return "client";
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Get rules that should execute for a specific trigger
|
|
356
|
+
*/
|
|
357
|
+
export function getRulesForField(
|
|
358
|
+
fieldName: string,
|
|
359
|
+
fieldRules: ProcessedSchema["fieldRules"],
|
|
360
|
+
classifiedRules: ProcessedSchema["rules"],
|
|
361
|
+
executionType: "client" | "server"
|
|
362
|
+
): ValidationRule[] {
|
|
363
|
+
const rules: ValidationRule[] = [];
|
|
364
|
+
const fieldRuleMap = fieldRules[fieldName];
|
|
365
|
+
|
|
366
|
+
if (!fieldRuleMap) return rules;
|
|
367
|
+
|
|
368
|
+
// Add validation rules (client-side only)
|
|
369
|
+
if (executionType === "client") {
|
|
370
|
+
fieldRuleMap.validation.forEach((ruleId) => {
|
|
371
|
+
const rule = classifiedRules.validation[ruleId];
|
|
372
|
+
if (rule) rules.push(rule);
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Add computation and business logic rules (server-side only)
|
|
377
|
+
if (executionType === "server") {
|
|
378
|
+
fieldRuleMap.computation.forEach((ruleId) => {
|
|
379
|
+
const rule = classifiedRules.computation[ruleId];
|
|
380
|
+
if (rule) rules.push(rule);
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
fieldRuleMap.businessLogic.forEach((ruleId) => {
|
|
384
|
+
const rule = classifiedRules.businessLogic[ruleId];
|
|
385
|
+
if (rule) rules.push(rule);
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return rules;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// ============================================================
|
|
393
|
+
// LEGACY SCHEMA SUPPORT
|
|
394
|
+
// ============================================================
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Convert legacy BackendSchema to BDO format
|
|
398
|
+
*/
|
|
399
|
+
export function convertLegacySchema(legacySchema: BackendSchema): BDOSchema {
|
|
400
|
+
return {
|
|
401
|
+
Id: "legacy_schema",
|
|
402
|
+
Name: "Legacy Schema",
|
|
403
|
+
Kind: "BusinessObject",
|
|
404
|
+
Description: "Converted from legacy schema format",
|
|
405
|
+
Rules: {
|
|
406
|
+
Validation: {},
|
|
407
|
+
Computation: {},
|
|
408
|
+
BusinessLogic: {},
|
|
409
|
+
},
|
|
410
|
+
Fields: Object.fromEntries(
|
|
411
|
+
Object.entries(legacySchema).map(([fieldName, field]) => [
|
|
412
|
+
fieldName,
|
|
413
|
+
{
|
|
414
|
+
...field,
|
|
415
|
+
Id: fieldName,
|
|
416
|
+
Name: field.Description || fieldName,
|
|
417
|
+
Validation: [], // Legacy format needs to be processed separately
|
|
418
|
+
},
|
|
419
|
+
])
|
|
420
|
+
),
|
|
421
|
+
RolePermission: {},
|
|
422
|
+
Roles: {},
|
|
423
|
+
};
|
|
424
|
+
}
|