data-primals-engine 1.2.2 → 1.2.4
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/CONTRIBUTING.md +91 -0
- package/README.md +33 -17
- package/client/src/App.jsx +0 -5
- package/client/src/ConditionBuilder.scss +34 -1
- package/client/src/ConditionBuilder2.jsx +179 -53
- package/client/src/ContentView.jsx +0 -3
- package/client/src/CronBuilder.jsx +0 -1
- package/client/src/CronPartBuilder.jsx +0 -2
- package/client/src/DashboardView.jsx +0 -5
- package/client/src/DataEditor.jsx +8 -211
- package/client/src/DataLayout.jsx +0 -1
- package/client/src/DataTable.jsx +1 -3
- package/client/src/Field.jsx +0 -5
- package/client/src/FlexBuilder.jsx +1 -1
- package/client/src/ModelCreatorField.jsx +1 -5
- package/client/src/RTE.jsx +1 -6
- package/client/src/RTETrans.jsx +0 -2
- package/client/src/RelationField.jsx +1 -1
- package/client/src/RelationValue.jsx +1 -2
- package/client/src/TourSpotlight.jsx +0 -2
- package/client/src/constants.js +1 -1
- package/client/src/filter.js +87 -0
- package/client/src/hooks/data.js +1 -3
- package/client/src/hooks/useTutorials.jsx +0 -1
- package/package.json +3 -3
- package/server.js +2 -2
- package/src/constants.js +2 -2
- package/src/defaultModels.js +1 -14
- package/src/email.js +9 -7
- package/src/engine.js +60 -20
- package/src/events.js +1 -1
- package/src/filter.js +221 -0
- package/src/index.js +1 -1
- package/src/middlewares/middleware-mongodb.js +0 -1
- package/src/modules/assistant.js +1 -3
- package/src/modules/bucket.js +3 -4
- package/src/modules/{data.js → data/data.js} +42 -59
- package/src/modules/data/index.js +1 -0
- package/src/modules/file.js +1 -1
- package/src/modules/mongodb.js +0 -1
- package/src/modules/user.js +1 -1
- package/src/modules/workflow.js +299 -133
- package/src/packs.js +249 -8
- package/test/data.backup.integration.test.js +7 -5
- package/test/data.integration.test.js +8 -6
- package/test/events.test.js +1 -1
- package/test/file.test.js +11 -17
- package/test/import_export.integration.test.js +38 -27
- package/test/model.integration.test.js +20 -21
- package/test/user.test.js +32 -25
- package/test/vm.test.js +51 -0
- package/test/workflow.integration.test.js +22 -14
- package/test/workflow.robustness.test.js +19 -9
- package/src/modules/test +0 -147
|
@@ -25,6 +25,7 @@ import Draggable from "./Draggable.jsx";
|
|
|
25
25
|
import CronBuilder from "./CronBuilder.jsx";
|
|
26
26
|
import RTETrans from "./RTETrans.jsx";
|
|
27
27
|
import uniqid from "uniqid";
|
|
28
|
+
import {isConditionMet} from "../../src/filter";
|
|
28
29
|
|
|
29
30
|
// ... (fonction getInputType) ...
|
|
30
31
|
// Fonction pour obtenir le type d'input HTML basé sur le type de champ du modèle
|
|
@@ -52,208 +53,6 @@ const getInputType = (fieldType) => {
|
|
|
52
53
|
}
|
|
53
54
|
};
|
|
54
55
|
|
|
55
|
-
/**
|
|
56
|
-
* Evaluates a single condition against form data.
|
|
57
|
-
* @param {object} currentModelDef - The definition of the current model.
|
|
58
|
-
* @param {object} condition - The condition to evaluate.
|
|
59
|
-
* @param {object} formData - The form data.
|
|
60
|
-
* @param {object[]} allModels - An array of all model definitions.
|
|
61
|
-
* @param {object} user - The current user.
|
|
62
|
-
* @returns {boolean} - True if the condition is met, false otherwise.
|
|
63
|
-
*/
|
|
64
|
-
const evaluateSingleCondition = (currentModelDef, condition, formData, allModels, user) => {
|
|
65
|
-
// Condition est directement un filtre MongoDB, donc on l'applique
|
|
66
|
-
// en utilisant les opérateurs et les valeurs qu'il contient.
|
|
67
|
-
|
|
68
|
-
if (!condition || typeof condition !== 'object') {
|
|
69
|
-
console.warn("[Client Eval] Condition is not an object:", condition);
|
|
70
|
-
return true; // Permissive default
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Si la condition contient des opérateurs logiques, on les gère ici
|
|
74
|
-
if (condition.$and || condition.$or || condition.$not || condition.$nor) {
|
|
75
|
-
console.warn("[Client Eval] Condition logique détectée dans evaluateSingleCondition, ce n'est pas attendu. Devrait être géré par isConditionMet.");
|
|
76
|
-
return true; // Permissive default
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (condition.$find) {
|
|
80
|
-
const fieldName = Object.keys(condition)[0];
|
|
81
|
-
const fieldValue = formData[fieldName];
|
|
82
|
-
const findCondition = condition.$find;
|
|
83
|
-
|
|
84
|
-
if (!Array.isArray(fieldValue)) return false;
|
|
85
|
-
|
|
86
|
-
return fieldValue.some(item => {
|
|
87
|
-
// Gestion spéciale pour la syntaxe $eq: ["$$this.field", value]
|
|
88
|
-
if (findCondition.$eq && Array.isArray(findCondition.$eq)) {
|
|
89
|
-
const [fieldPath, value] = findCondition.$eq;
|
|
90
|
-
if (fieldPath.startsWith("$$this.")) {
|
|
91
|
-
const fieldToCheck = fieldPath.replace("$$this.", "");
|
|
92
|
-
return item[fieldToCheck] == value;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Sinon, évaluation normale
|
|
97
|
-
const tempData = { ...item };
|
|
98
|
-
return evaluateSingleCondition(currentModelDef, findCondition, tempData, allModels, user);
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Si la condition contient un opérateur $exists, on le gère ici
|
|
103
|
-
if (condition.$exists !== undefined) {
|
|
104
|
-
const fieldName = Object.keys(condition)[0]; // Récupérer le nom du champ
|
|
105
|
-
const shouldExist = condition.$exists; // Récupérer la valeur de $exists (true ou false)
|
|
106
|
-
const exists = Object.prototype.hasOwnProperty.call(formData, fieldName) && formData[fieldName] !== undefined && formData[fieldName] !== null;
|
|
107
|
-
return exists === shouldExist;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Si la condition contient un opérateur $find, on le gère ici
|
|
111
|
-
if (condition.$find) {
|
|
112
|
-
// Récupérer le nom du champ
|
|
113
|
-
const fieldName = Object.keys(condition)[0];
|
|
114
|
-
const fieldValue = formData[fieldName];
|
|
115
|
-
try {
|
|
116
|
-
// Assuming evaluateSingleCondition handles $find
|
|
117
|
-
return evaluateSingleCondition(currentModelDef, condition.$find, formData, allModels, user);
|
|
118
|
-
} catch (error) {
|
|
119
|
-
console.error("Error evaluating $find condition:", condition, error);
|
|
120
|
-
return false;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Récupérer le nom du champ et la condition
|
|
125
|
-
const fieldName = Object.keys(condition)[0];
|
|
126
|
-
const fieldValue = condition[fieldName];
|
|
127
|
-
|
|
128
|
-
// Récupérer la définition du champ
|
|
129
|
-
const fieldDef = currentModelDef?.fields.find(f => f.name === fieldName);
|
|
130
|
-
|
|
131
|
-
// Si la définition du champ n'est pas trouvée, on retourne true
|
|
132
|
-
if (!fieldDef) {
|
|
133
|
-
console.warn(`[Client Eval] Field definition not found for field: ${fieldName}`);
|
|
134
|
-
return true; // Permissive default
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
let targetValue = formData[fieldName];
|
|
138
|
-
let processedConditionValue = fieldValue;
|
|
139
|
-
|
|
140
|
-
// 1. Handle $exists (on the first field)
|
|
141
|
-
// 2. Convert condition value based on operator's expected input type
|
|
142
|
-
const fieldType = fieldDef?.type; // Type of the first field
|
|
143
|
-
|
|
144
|
-
try {
|
|
145
|
-
processedConditionValue = convertValueType(fieldValue, fieldType);
|
|
146
|
-
} catch (e) {
|
|
147
|
-
logClientEvalWarning(`Error converting value type: ${e.message}`, condition);
|
|
148
|
-
return false;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return evaluateComparison(fieldValue, targetValue, processedConditionValue, condition);
|
|
152
|
-
|
|
153
|
-
function logClientEvalWarning(message, details) {
|
|
154
|
-
console.warn(`[Client Eval] ${message}:`, details);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function convertValueType(value, inputType) {
|
|
158
|
-
switch (inputType) {
|
|
159
|
-
case 'number':
|
|
160
|
-
const numValue = parseFloat(value);
|
|
161
|
-
if (isNaN(numValue)) {
|
|
162
|
-
throw new Error(`Invalid number value: ${value}`);
|
|
163
|
-
}
|
|
164
|
-
return numValue;
|
|
165
|
-
case 'boolean':
|
|
166
|
-
return String(value).toLowerCase() === 'true';
|
|
167
|
-
case 'csv':
|
|
168
|
-
return String(value).split(',').map(item => item.trim()).filter(Boolean);
|
|
169
|
-
case 'text':
|
|
170
|
-
default:
|
|
171
|
-
return String(value);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function evaluateComparison(operator, targetValue, processedConditionValue, condition) {
|
|
176
|
-
try {
|
|
177
|
-
switch (typeof operator === 'object' ? Object.keys(operator)[0] : null) {
|
|
178
|
-
case '$eq': return targetValue == processedConditionValue;
|
|
179
|
-
case '$ne': return targetValue != processedConditionValue;
|
|
180
|
-
case '$gt': return targetValue > processedConditionValue;
|
|
181
|
-
case '$lt': return targetValue < processedConditionValue;
|
|
182
|
-
case '$gte': return targetValue >= processedConditionValue;
|
|
183
|
-
case '$lte': return targetValue <= processedConditionValue;
|
|
184
|
-
case '$regex':
|
|
185
|
-
if (typeof targetValue !== 'string') return false;
|
|
186
|
-
if (typeof processedConditionValue !== 'string') return false;
|
|
187
|
-
try {
|
|
188
|
-
const regex = new RegExp(processedConditionValue, 'i');
|
|
189
|
-
return regex.test(targetValue);
|
|
190
|
-
} catch (e) {
|
|
191
|
-
logClientEvalWarning(`Invalid regex pattern: ${processedConditionValue}`, condition);
|
|
192
|
-
return false;
|
|
193
|
-
}
|
|
194
|
-
case '$in':
|
|
195
|
-
return Array.isArray(processedConditionValue) && processedConditionValue.includes(String(targetValue));
|
|
196
|
-
case '$nin':
|
|
197
|
-
return !Array.isArray(processedConditionValue) || !processedConditionValue.includes(String(targetValue));
|
|
198
|
-
default:
|
|
199
|
-
logClientEvalWarning(`Unhandled operator in client evaluation logic: ${operator}`, condition);
|
|
200
|
-
return true; // Permissive default
|
|
201
|
-
}
|
|
202
|
-
} catch (evalError) {
|
|
203
|
-
logClientEvalWarning(`Error during client condition evaluation: ${operator}, targetValue=${targetValue}, processedConditionValue=${processedConditionValue}`, condition);
|
|
204
|
-
return false;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
export const isConditionMet = (model, cond, formData, allModels, user) => {
|
|
210
|
-
const isNode = (v) => typeof v === 'object' && v !== null;
|
|
211
|
-
|
|
212
|
-
const condition = cond;
|
|
213
|
-
|
|
214
|
-
if (!condition) return true;
|
|
215
|
-
|
|
216
|
-
if (condition.$and && Array.isArray(condition.$and)) {
|
|
217
|
-
if (condition.$and.length === 0) return true;
|
|
218
|
-
return condition.$and.every(sub => isConditionMet(model, sub, formData, allModels, user));
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
if (condition.$or && Array.isArray(condition.$or)) {
|
|
222
|
-
if (condition.$or.length === 0) return false;
|
|
223
|
-
return condition.$or.some(sub => isConditionMet(model, sub, formData, allModels, user));
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (condition.$not) {
|
|
227
|
-
return !isConditionMet(model, condition.$not, formData, allModels, user);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
if (condition.$nor && Array.isArray(condition.$nor)) {
|
|
231
|
-
if (condition.$nor.length === 0) return true;
|
|
232
|
-
return !condition.$nor.some(sub => isConditionMet(model, sub, formData, allModels, user));
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
if (condition.$find) {
|
|
236
|
-
try {
|
|
237
|
-
// Assuming evaluateSingleCondition handles $find
|
|
238
|
-
return evaluateSingleCondition(model, condition, formData, allModels, user);
|
|
239
|
-
} catch (error) {
|
|
240
|
-
console.error("Error evaluating $find condition:", condition, error);
|
|
241
|
-
return false;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if (condition.path && condition.op) {
|
|
246
|
-
try {
|
|
247
|
-
return evaluateSingleCondition(model, condition, formData, allModels, user);
|
|
248
|
-
} catch (error) {
|
|
249
|
-
console.error("Error evaluating condition:", condition, error);
|
|
250
|
-
return false;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
console.warn("Unknown condition format:", condition);
|
|
255
|
-
return true;
|
|
256
|
-
};
|
|
257
56
|
|
|
258
57
|
export const DataEditor = forwardRef(function MyDataEditor({
|
|
259
58
|
isLoading,
|
|
@@ -263,7 +62,6 @@ export const DataEditor = forwardRef(function MyDataEditor({
|
|
|
263
62
|
formData,
|
|
264
63
|
setFormData, record, setRecord}, ref){
|
|
265
64
|
|
|
266
|
-
const [focusedField, setFocusedField] = useState({});
|
|
267
65
|
const {me} = useAuthContext()
|
|
268
66
|
const {models} = useModelContext()
|
|
269
67
|
|
|
@@ -304,7 +102,7 @@ export const DataEditor = forwardRef(function MyDataEditor({
|
|
|
304
102
|
case 'textarea':
|
|
305
103
|
return <textarea key={field.name} {...inputProps} />
|
|
306
104
|
case 'richtext':
|
|
307
|
-
return <RTE help={
|
|
105
|
+
return <RTE help={t('field_'+model.name+'_'+field.name+'_hint', field.hint || '')} key={field.name} {...inputProps} field={field} name={formData._id} />;
|
|
308
106
|
case 'richtext_t':
|
|
309
107
|
return <RTETrans
|
|
310
108
|
key={field.name}
|
|
@@ -345,7 +143,6 @@ export const DataEditor = forwardRef(function MyDataEditor({
|
|
|
345
143
|
// C'est un nom de modèle statique
|
|
346
144
|
builderModelName = field.targetModel;
|
|
347
145
|
}
|
|
348
|
-
console.log({builderModelName})
|
|
349
146
|
}
|
|
350
147
|
return <div className={"flex flex-1"} style={{width:'100%'}} key={field.name}>
|
|
351
148
|
{currentViewMode !== 'builder' && ( <div className="condition-builder-toggle">
|
|
@@ -420,10 +217,10 @@ export const DataEditor = forwardRef(function MyDataEditor({
|
|
|
420
217
|
inputProps["min"] = field.min;
|
|
421
218
|
if( field.max)
|
|
422
219
|
inputProps["max"] = field.max;
|
|
423
|
-
return <NumberField help={
|
|
220
|
+
return <NumberField help={t('field_'+model.name+'_'+field.name+'_hint', field.hint || '')} unit={field.unit} key={field.name} {...inputProps} onChange={(e) => handleChange({name: field.name, value: parseFloat(e.target.value.replace(',', '.'))})} />
|
|
424
221
|
case 'relation':
|
|
425
222
|
return (
|
|
426
|
-
<RelationField
|
|
223
|
+
<RelationField help={t('field_'+model.name+'_'+field.name+'_hint', field.hint || '')} key={field.name} model={model} field={field} value={value} onChange={(e) => {
|
|
427
224
|
handleChange(e)
|
|
428
225
|
}} refreshTime={refreshTime} />
|
|
429
226
|
);
|
|
@@ -456,8 +253,8 @@ export const DataEditor = forwardRef(function MyDataEditor({
|
|
|
456
253
|
const displayValue = (typeof value === 'object' && value !== null) ? value.key : (value || '');
|
|
457
254
|
|
|
458
255
|
return <TextField
|
|
459
|
-
help={
|
|
460
|
-
|
|
256
|
+
help={t('field_' + model.name + '_' + field.name + '_hint', field.hint || '')}
|
|
257
|
+
key={field.name}
|
|
461
258
|
type={getInputType(field.type)} {...inputProps}
|
|
462
259
|
value={displayValue}
|
|
463
260
|
onChange={(e) => handleChange({name: field.name, value: e.target.value})} />
|
|
@@ -467,9 +264,9 @@ export const DataEditor = forwardRef(function MyDataEditor({
|
|
|
467
264
|
case 'color':
|
|
468
265
|
return <ColorField help={t('field_'+model.name+'_'+field.name+'_hint', field.hint || '')} key={field.name} name={field.name} value={value} onChange={handleChange} />
|
|
469
266
|
case 'email':
|
|
470
|
-
return <EmailField
|
|
267
|
+
return <EmailField help={t('field_'+model.name+'_'+field.name+'_hint', field.hint || '')} key={field.name} type={getInputType(field.type)} {...inputProps} onChange={(e) => handleChange({name: field.name, value: e.target.value})} />
|
|
471
268
|
default:
|
|
472
|
-
return <TextField
|
|
269
|
+
return <TextField help={t('field_'+model.name+'_'+field.name+'_hint', field.hint || '')} key={field.name} type={getInputType(field.type)} {...inputProps} onChange={(e) => handleChange({name: field.name, value: e.target.value})} />
|
|
473
270
|
}
|
|
474
271
|
}
|
|
475
272
|
|
package/client/src/DataTable.jsx
CHANGED
|
@@ -43,7 +43,6 @@ import RelationValue from "./RelationValue.jsx";
|
|
|
43
43
|
import {FaGear, FaPencil, FaTriangleExclamation} from "react-icons/fa6";
|
|
44
44
|
import RestoreConfirmationModal from "./RestoreConfirmationModal.jsx";
|
|
45
45
|
import {event_trigger, isLightColor} from "../../src/core.js";
|
|
46
|
-
import {isConditionMet} from "./DataEditor.jsx";
|
|
47
46
|
import {Tooltip} from "react-tooltip";
|
|
48
47
|
import ExportDialog from "./ExportDialog.jsx";
|
|
49
48
|
import Captions from "yet-another-react-lightbox/plugins/captions";
|
|
@@ -58,6 +57,7 @@ import PackGallery from "./PackGallery.jsx";
|
|
|
58
57
|
import {HiddenableCell} from "./HiddenableCell.jsx";
|
|
59
58
|
import ConditionBuilder from "./ConditionBuilder.jsx";
|
|
60
59
|
import {pagedFilterToMongoConds} from "./filter.js";
|
|
60
|
+
import {isConditionMet} from "../../src/filter";
|
|
61
61
|
|
|
62
62
|
// Ajoutez cette constante pour la clé de sessionStorage
|
|
63
63
|
const SESSION_STORAGE_IMPORT_JOBS_KEY = 'activeImportJobs';
|
|
@@ -685,10 +685,8 @@ export function DataTable({
|
|
|
685
685
|
/>
|
|
686
686
|
<ExportDialog isOpen={showExportDialog} onClose={() => {
|
|
687
687
|
setExportDialogVisible(false);
|
|
688
|
-
console.log("close")
|
|
689
688
|
}} availableModels={models} currentModel={selectedModel.name} hasSelection={true} onExport={(data)=>{
|
|
690
689
|
exportMutation(data);
|
|
691
|
-
console.log(data);
|
|
692
690
|
}} />
|
|
693
691
|
{showPackGallery && (
|
|
694
692
|
<Dialog isClosable={true} isModal={true} onClose={() => setShowPackGallery(false)}>
|
package/client/src/Field.jsx
CHANGED
|
@@ -33,9 +33,7 @@ import { docco } from 'react-syntax-highlighter/dist/esm/styles/hljs';
|
|
|
33
33
|
|
|
34
34
|
import { PhoneInput } from 'react-international-phone';
|
|
35
35
|
import 'react-international-phone/style.css';
|
|
36
|
-
import CodeMirror, {basicSetup} from "@uiw/react-codemirror";
|
|
37
36
|
import {useAuthContext} from "./contexts/AuthContext.jsx";
|
|
38
|
-
import {maxStringLength} from "data-primals-engine/constants";
|
|
39
37
|
|
|
40
38
|
export const Form = ({
|
|
41
39
|
name,
|
|
@@ -893,7 +891,6 @@ const FileField = ({ inputProps, value, onChange, name, mimeTypes, maxSize, mult
|
|
|
893
891
|
}
|
|
894
892
|
}, [value]);
|
|
895
893
|
|
|
896
|
-
console.log(fileInfos)
|
|
897
894
|
return (
|
|
898
895
|
<div className="field field-file">
|
|
899
896
|
<input
|
|
@@ -1421,12 +1418,10 @@ export const ModelField = ({field, disableable=false, showModel=true, value, fie
|
|
|
1421
1418
|
const itemsFields = [...models.find(f=>f.name === modelValue && me?.username === f._user)?.fields.map(m => ({label: m.name, value: m.name})) || []];
|
|
1422
1419
|
|
|
1423
1420
|
useEffect(() => {
|
|
1424
|
-
console.log({value})
|
|
1425
1421
|
setModelValue(value)
|
|
1426
1422
|
}, [value]);
|
|
1427
1423
|
|
|
1428
1424
|
useEffect(() => {
|
|
1429
|
-
console.log(modelValue)
|
|
1430
1425
|
onChange({name: field.name, value: modelValue});
|
|
1431
1426
|
}, [modelValue]);
|
|
1432
1427
|
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
clearMappingsRecursive
|
|
20
20
|
} from './FlexTreeUtils.js';
|
|
21
21
|
import {Dialog, DialogProvider} from "./Dialog.jsx";
|
|
22
|
-
import {safeAssignObject} from "
|
|
22
|
+
import {safeAssignObject} from "../../src/core";
|
|
23
23
|
|
|
24
24
|
const FlexBuilder = ({ initialConfig = null, models = [], onChange, data = [], lang = 'fr' }) => {
|
|
25
25
|
const { me: user } = useAuthContext();
|
|
@@ -43,7 +43,6 @@ const ModelCreatorField = ({model, handleRenameField, handleRemoveField, handleU
|
|
|
43
43
|
const [showMore, setMoreVisible] = useState(false);
|
|
44
44
|
const hint = (description) => t(description, '') && <div className="hint-icon"><FaCircleInfo data-tooltip-id={`tooltipHint`} data-tooltip-content={description} /></div>
|
|
45
45
|
|
|
46
|
-
const [focusedField, setFocusedField] = useState({});
|
|
47
46
|
return (
|
|
48
47
|
<div className="field-edit">
|
|
49
48
|
|
|
@@ -73,9 +72,7 @@ const ModelCreatorField = ({model, handleRenameField, handleRemoveField, handleU
|
|
|
73
72
|
newFields[index].name = e.target.value;
|
|
74
73
|
setFields(newFields);
|
|
75
74
|
}}
|
|
76
|
-
help={
|
|
77
|
-
onFocus={() => setFocusedField(field)}
|
|
78
|
-
onBlur={() => setFocusedField(null)}
|
|
75
|
+
help={t('modelcreator.name.hint')}
|
|
79
76
|
required
|
|
80
77
|
/>
|
|
81
78
|
{!(!modelLocked && isLocalUser(me) && field.locked) && !field._isNewField && (
|
|
@@ -206,7 +203,6 @@ const ModelCreatorField = ({model, handleRenameField, handleRemoveField, handleU
|
|
|
206
203
|
initialSteps={field.calculation?.steps || []}
|
|
207
204
|
onCalculationChange={(calc) => {
|
|
208
205
|
const newFields = [...fields];
|
|
209
|
-
console.log({calc});
|
|
210
206
|
newFields[index].calculation = calc; // 'index' est l'index du champ calculé dans 'fields'
|
|
211
207
|
setFields(newFields);
|
|
212
208
|
}}
|
package/client/src/RTE.jsx
CHANGED
|
@@ -28,7 +28,7 @@ import css from 'highlight.js/lib/languages/css'
|
|
|
28
28
|
import js from 'highlight.js/lib/languages/javascript'
|
|
29
29
|
import ts from 'highlight.js/lib/languages/typescript'
|
|
30
30
|
import html from 'highlight.js/lib/languages/xml'
|
|
31
|
-
import {escapeHtml
|
|
31
|
+
import {escapeHtml} from "../../src/core";
|
|
32
32
|
// create a lowlight instance
|
|
33
33
|
const lowlight = createLowlight(all)
|
|
34
34
|
|
|
@@ -293,11 +293,6 @@ export const ExtendedImage = Image.extend({
|
|
|
293
293
|
}
|
|
294
294
|
})
|
|
295
295
|
|
|
296
|
-
const CodeBlockComponent = ({children, ...rest}) => {
|
|
297
|
-
console.log(rest);
|
|
298
|
-
return children;
|
|
299
|
-
};
|
|
300
|
-
|
|
301
296
|
const extensions = [
|
|
302
297
|
HardBreak,
|
|
303
298
|
CodeBlock,
|
package/client/src/RTETrans.jsx
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
// Dans C:/Dev/hackersonline-engine/client/src/RTETrans.jsx (Nouveau fichier)
|
|
2
1
|
import React, { useState, useEffect } from 'react';
|
|
3
2
|
import { useTranslation } from 'react-i18next';
|
|
4
3
|
import { RTE } from './RTE.jsx';
|
|
5
|
-
import {useModelContext} from "./contexts/ModelContext.jsx";
|
|
6
4
|
import {useQuery} from "react-query";
|
|
7
5
|
import {getUserId} from "../../src/data.js";
|
|
8
6
|
import {useAuthContext} from "./contexts/AuthContext.jsx";
|
|
@@ -3,7 +3,7 @@ import { useQuery, useQueryClient } from 'react-query';
|
|
|
3
3
|
import { useModelContext } from './contexts/ModelContext.jsx';
|
|
4
4
|
import { TextField } from './Field.jsx';
|
|
5
5
|
import { useAuthContext } from './contexts/AuthContext.jsx';
|
|
6
|
-
import { getDataAsString
|
|
6
|
+
import { getDataAsString } from '../../src/data.js';
|
|
7
7
|
import { FaEdit, FaTrash } from 'react-icons/fa';
|
|
8
8
|
import Button from './Button.jsx';
|
|
9
9
|
import { mainFieldsTypes } from "../../src/constants.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
|
2
2
|
import { useQuery } from 'react-query';
|
|
3
3
|
import { useModelContext } from './contexts/ModelContext.jsx';
|
|
4
|
-
import {getDataAsString, getUserId} from '
|
|
4
|
+
import {getDataAsString, getUserId} from '../../src/data.js';
|
|
5
5
|
import { Trans, useTranslation } from 'react-i18next';
|
|
6
6
|
import {Dialog, DialogProvider} from './Dialog.jsx';
|
|
7
7
|
import {FaMagnifyingGlass} from "react-icons/fa6";
|
|
@@ -133,7 +133,6 @@ const RelationValue = ({ field, data, align }) => {
|
|
|
133
133
|
} catch (e) {
|
|
134
134
|
console.log(e);
|
|
135
135
|
}
|
|
136
|
-
//console.log(model, rel, displayValue)
|
|
137
136
|
const bgColor = // Returns a bright color in RGB
|
|
138
137
|
randomColor({
|
|
139
138
|
seed: rel._hash+rel._id,
|
|
@@ -61,8 +61,6 @@ function TourSpotlight({ name, steps = [], isOpen, onComplete, onClose, initialS
|
|
|
61
61
|
useEffect(() => {
|
|
62
62
|
if (!isOpen || !name) return;
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
console.log({currentStep})
|
|
66
64
|
if (!currentStep?.selector) {
|
|
67
65
|
console.warn(`[TourSpotlight] Élément cible non défini pour le tour ${name}`);
|
|
68
66
|
return;
|
package/client/src/constants.js
CHANGED
|
@@ -53,7 +53,7 @@ export const MONGO_OPERATORS = {
|
|
|
53
53
|
export const profiles = {
|
|
54
54
|
'personal': ['contact', 'location', 'imageGallery', 'budget', 'currency', 'taxonomy'], // budget,
|
|
55
55
|
'developer': ['alert','endpoint','request','webpage', 'content', 'taxonomy', 'resource', 'translation', 'contact', 'location', 'channel', 'lang', 'token', 'message', 'ticket', 'user', 'permission', 'role'],
|
|
56
|
-
'company': ['alert','request','location', '
|
|
56
|
+
'company': ['alert','request','location', 'order', 'currency', 'product', 'cart', 'cartItem', 'invoice', 'message', 'user', 'role', 'permission', 'token','translation', 'lang', 'webpage', 'content', 'taxonomy', 'contact', 'resource', 'accountingExercise', 'accountingLineItem', 'accountingEntry', 'employee', 'kpi', 'dashboard'],
|
|
57
57
|
'engineer': ['alert','endpoint','request','dashboard', 'kpi', 'user', 'role', 'token', 'permission', 'workflow', 'workflowRun', 'workflowStep', "channel", "message", 'workflowAction', 'workflowTrigger']
|
|
58
58
|
}
|
|
59
59
|
|
package/client/src/filter.js
CHANGED
|
@@ -62,6 +62,93 @@ export const MONGO_CALC_OPERATORS = {
|
|
|
62
62
|
$minute: { label: 'minute', multi: false, isDate: true },
|
|
63
63
|
$second: { label: 'second', multi: false, isDate: true },
|
|
64
64
|
$millisecond: { label: 'ms', multi: false, isDate: true },
|
|
65
|
+
// Date operators
|
|
66
|
+
$dateAdd: {
|
|
67
|
+
label: 'Add to date',
|
|
68
|
+
description: 'Adds a duration to a date',
|
|
69
|
+
isDate: true,
|
|
70
|
+
specialStructure: true,
|
|
71
|
+
args: [
|
|
72
|
+
{ name: 'startDate', label: 'Start Date', type: 'date' },
|
|
73
|
+
{ name: 'unit', label: 'Unit', type: 'select', options: ['year', 'month', 'day', 'hour', 'minute', 'second'] },
|
|
74
|
+
{ name: 'amount', label: 'Amount', type: 'number' },
|
|
75
|
+
{ name: 'timezone', label: 'Timezone', type: 'text', optional: true }
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
$dateSubtract: {
|
|
79
|
+
label: 'Subtract from date',
|
|
80
|
+
description: 'Subtracts a duration from a date',
|
|
81
|
+
isDate: true,
|
|
82
|
+
specialStructure: true,
|
|
83
|
+
args: [
|
|
84
|
+
{ name: 'startDate', label: 'Start Date', type: 'date' },
|
|
85
|
+
{ name: 'unit', label: 'Unit', type: 'select', options: ['year', 'month', 'day', 'hour', 'minute', 'second'] },
|
|
86
|
+
{ name: 'amount', label: 'Amount', type: 'number' },
|
|
87
|
+
{ name: 'timezone', label: 'Timezone', type: 'text', optional: true }
|
|
88
|
+
]
|
|
89
|
+
},
|
|
90
|
+
$dateDiff: {
|
|
91
|
+
label: 'Date Difference',
|
|
92
|
+
description: 'Calculates the difference between two dates in specified units',
|
|
93
|
+
isDate: true,
|
|
94
|
+
specialStructure: true,
|
|
95
|
+
args: [
|
|
96
|
+
{
|
|
97
|
+
name: 'startDate',
|
|
98
|
+
label: 'Start Date',
|
|
99
|
+
type: 'date',
|
|
100
|
+
description: 'The starting date (inclusive)'
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'endDate',
|
|
104
|
+
label: 'End Date',
|
|
105
|
+
type: 'date',
|
|
106
|
+
description: 'The ending date (exclusive)'
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: 'unit',
|
|
110
|
+
label: 'Unit',
|
|
111
|
+
type: 'select',
|
|
112
|
+
options: ['year', 'month', 'week', 'day', 'hour', 'minute', 'second'],
|
|
113
|
+
description: 'The unit for the result'
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: 'timezone',
|
|
117
|
+
label: 'Timezone',
|
|
118
|
+
type: 'text',
|
|
119
|
+
optional: true,
|
|
120
|
+
description: 'The timezone (e.g. "Europe/Paris")'
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
},
|
|
124
|
+
$dateToString: {
|
|
125
|
+
label: 'Format date as string',
|
|
126
|
+
description: 'Formats a date as a string',
|
|
127
|
+
isDate: true,
|
|
128
|
+
specialStructure: true,
|
|
129
|
+
args: [
|
|
130
|
+
{
|
|
131
|
+
name: 'date',
|
|
132
|
+
label: 'Date',
|
|
133
|
+
type: 'date'
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: 'format',
|
|
137
|
+
label: 'Format',
|
|
138
|
+
optional: true,
|
|
139
|
+
type: 'text'
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: 'timezone',
|
|
143
|
+
label: 'Timezone',
|
|
144
|
+
type: 'text',
|
|
145
|
+
optional: true,
|
|
146
|
+
description: 'The timezone (e.g. "Europe/Paris")'
|
|
147
|
+
}
|
|
148
|
+
]
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
// Converters
|
|
65
152
|
$toBool: { label: 'toBool', multi: false, converter: true },
|
|
66
153
|
$toString: { label: 'toString', multi: false, converter: true },
|
|
67
154
|
$toInt: { label: 'toInt', multi: false, converter: true },
|
package/client/src/hooks/data.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import {useQuery} from "react-query";
|
|
2
|
-
import {getObjectHash} from "data-primals-engine/core";
|
|
3
2
|
import {useAuthContext} from "../contexts/AuthContext.jsx";
|
|
4
|
-
import {
|
|
3
|
+
import {getUserId} from "../../../src/data";
|
|
5
4
|
import {useTranslation} from "react-i18next";
|
|
6
5
|
import {useEffect, useState} from "react";
|
|
7
|
-
import {useUI} from "../contexts/UIContext.jsx";
|
|
8
6
|
import {useNotificationContext} from "../NotificationProvider.jsx";
|
|
9
7
|
|
|
10
8
|
export const useData = (model, filter, options) => {
|
|
@@ -138,7 +138,6 @@ export const useTutorials = () => {
|
|
|
138
138
|
const tutorial = tutorials.find(t => t._id === currentActiveState.id);
|
|
139
139
|
if (!tutorial) break;
|
|
140
140
|
|
|
141
|
-
console.log({currentActiveState})
|
|
142
141
|
const stageConfig = tutorial.stages.find(s => s.stage === currentActiveState.stage);
|
|
143
142
|
if (!stageConfig) {
|
|
144
143
|
console.log('[Tutoriels] Fin du tutoriel, attribution des récompenses.');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "data-primals-engine",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"description": "data-primals-engine is a package responsible from handling large amount of data using MongoDB in a practical and performant way. It can also get workflow models working (for automation), and fully supports internationalisation. It also has integrated AI assistant.",
|
|
5
5
|
"main": "src/engine.js",
|
|
6
6
|
"type": "module",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"exports": {
|
|
41
41
|
".": "./src/index.js",
|
|
42
|
-
"./modules/*": "./src/modules
|
|
42
|
+
"./modules/*": "./src/modules/*/index.js",
|
|
43
43
|
"./client": "./client/index.js",
|
|
44
44
|
"./*": "./src/*.js"
|
|
45
45
|
},
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@langchain/core": "^0.3.66",
|
|
53
|
+
"@langchain/deepseek": "^0.1.0",
|
|
53
54
|
"@langchain/google-genai": "^0.2.16",
|
|
54
55
|
"@langchain/openai": "^0.6.3",
|
|
55
56
|
"archiver": "^7.0.1",
|
|
@@ -62,7 +63,6 @@
|
|
|
62
63
|
"cookie-parser": "^1.4.7",
|
|
63
64
|
"cronstrue": "^3.2.0",
|
|
64
65
|
"csv-parse": "^6.1.0",
|
|
65
|
-
"data-primals-engine": "^1.0.14",
|
|
66
66
|
"date-fns": "^4.1.0",
|
|
67
67
|
"express-csrf-double-submit-cookie": "^2.0.0",
|
|
68
68
|
"express-formidable": "^1.2.0",
|
package/server.js
CHANGED
|
@@ -8,7 +8,7 @@ import {Config, Engine, BenchmarkTool, GameObject, Logger} from "./src/index.js"
|
|
|
8
8
|
import sirv from "sirv";
|
|
9
9
|
import express from "express";
|
|
10
10
|
|
|
11
|
-
Config.Set("modules", ["
|
|
11
|
+
Config.Set("modules", ["data", "mongodb", "file", "bucket", "workflow","user", "assistant", "swagger"])
|
|
12
12
|
Config.Set("middlewares", []);
|
|
13
13
|
|
|
14
14
|
const bench = GameObject.Create("Benchmark");
|
|
@@ -30,7 +30,7 @@ if (process.argv.length === 3) {
|
|
|
30
30
|
const port = process.env.PORT || 7633;
|
|
31
31
|
engine.start(port, async (r) => {
|
|
32
32
|
const logger = engine.getComponent(Logger);
|
|
33
|
-
console.log("Server started on port" + port);
|
|
33
|
+
console.log("Server started on port " + port);
|
|
34
34
|
timer.stop();
|
|
35
35
|
|
|
36
36
|
});
|
package/src/constants.js
CHANGED
|
@@ -66,7 +66,7 @@ export const awsDefaultConfig = {
|
|
|
66
66
|
export const emailDefaultConfig = {
|
|
67
67
|
from: "Support - data@primals.net <data@primals.net>",
|
|
68
68
|
host: 'smtp.mydomain.tld',
|
|
69
|
-
port:
|
|
69
|
+
port: 587,
|
|
70
70
|
secure: false,
|
|
71
71
|
user: 'user',
|
|
72
72
|
pass: 'password'
|
|
@@ -288,7 +288,7 @@ metaModels['messaging'] = { load: ['alert','ticket', 'message', 'channel'], 'req
|
|
|
288
288
|
metaModels['eshopping'] = { load: [
|
|
289
289
|
'order', 'currency', 'product', 'productVariant', 'discount', 'cart', 'cartItem',
|
|
290
290
|
'brand', 'return', 'review', 'stock', 'returnItem', 'userSubscription',
|
|
291
|
-
'warehouse', 'shipment', '
|
|
291
|
+
'warehouse', 'shipment', 'stockAlert', 'invoice'],
|
|
292
292
|
'require': ['i18n', 'users', 'messaging'] };
|
|
293
293
|
metaModels['workflow'] = { load: ['env', 'workflow', 'workflowRun', 'workflowAction', 'workflowStep', 'workflowTrigger']};
|
|
294
294
|
metaModels['erp'] = { load: [ 'accountingExercise', 'accountingLineItem', 'accountingEntry', 'employee', 'dashboard', 'kpi'] };
|