data-primals-engine 1.6.1 → 1.6.2

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.
@@ -1,7 +1,7 @@
1
1
  import React, {useState, useEffect} from 'react';
2
2
  import { useModelContext } from './contexts/ModelContext.jsx';
3
3
  import { useAuthContext } from './contexts/AuthContext.jsx';
4
- import {Trans, useTranslation} from 'react-i18next';
4
+ import {Trans, useTranslation} from '../../src/i18n.js';
5
5
  import {FaArrowDown, FaInfo} from 'react-icons/fa';
6
6
  import { CheckboxField } from './Field.jsx';
7
7
  import Button from './Button.jsx';
@@ -30,7 +30,7 @@ const ViewSwitcher = ({ currentView, onViewChange, configuredViews, onConfigureV
30
30
  title={t(view.labelKey, view.defaultLabel)}
31
31
  >
32
32
  {view.icon}
33
- <span className="hidden md:inline-block ml-2">{t(view.labelKey, view.defaultLabel)}</span>
33
+ {/*<span className="hidden md:inline-block ml-2">{t(view.labelKey, view.defaultLabel)}</span>*/}
34
34
  </Button>
35
35
  {/* AJOUT : Bouton de configuration pour la vue active */}
36
36
  {isActive && view.id !== 'table' && (
@@ -0,0 +1,317 @@
1
+ import React, { useState, useCallback, useEffect, useRef } from 'react';
2
+ import ReactFlow, {
3
+ MiniMap,
4
+ Controls,
5
+ Background,
6
+ useNodesState,
7
+ useEdgesState,
8
+ addEdge
9
+ } from 'reactflow';
10
+ import 'reactflow/dist/style.css';
11
+ import { useModelContext } from './contexts/ModelContext';
12
+ import { useAuthContext } from './contexts/AuthContext';
13
+ import { Trans, useTranslation } from 'react-i18next';
14
+ import { CodeField } from "./Field.jsx";
15
+ import { useQuery } from 'react-query';
16
+
17
+ import "./WorkflowEditor.scss"
18
+ // --- Custom Node Types (à créer) ---
19
+ // import TriggerNode from './nodes/TriggerNode';
20
+ // import StepNode from './nodes/StepNode';
21
+
22
+ const nodeTypes = {
23
+ // trigger: TriggerNode,
24
+ // step: StepNode,
25
+ };
26
+
27
+ const WorkflowEditor = ({ workflowId }) => {
28
+ const { t } = useTranslation();
29
+ const { me } = useAuthContext();
30
+ const { models } = useModelContext();
31
+
32
+ const [nodes, setNodes, onNodesChange] = useNodesState([]);
33
+ const [edges, setEdges, onEdgesChange] = useEdgesState([]);
34
+ const [selectedNode, setSelectedNode] = useState(null);
35
+ const [panelWidth, setPanelWidth] = useState(300);
36
+ const resizeData = useRef({ isResizing: false, initialX: 0, initialWidth: 0 });
37
+
38
+ const handleMouseDown = (e) => {
39
+ e.preventDefault();
40
+ resizeData.current = {
41
+ isResizing: true,
42
+ initialX: e.clientX,
43
+ initialWidth: panelWidth
44
+ };
45
+ document.addEventListener('mousemove', handleMouseMove);
46
+ document.addEventListener('mouseup', handleMouseUp);
47
+ };
48
+
49
+ const handleMouseMove = useCallback((e) => {
50
+ if (!resizeData.current.isResizing) return; // Utilise la référence qui ne change pas
51
+ const dx = e.clientX - resizeData.current.initialX; // Calcule le delta depuis le clic initial
52
+ const newWidth = resizeData.current.initialWidth - dx; // Applique le delta à la largeur initiale
53
+ setPanelWidth(Math.max(200, Math.min(newWidth, 800))); // Applique les limites
54
+ }, []); // Plus besoin de dépendances, la logique est entièrement contenue dans la ref
55
+
56
+
57
+ const handleMouseUp = () => {
58
+ resizeData.current.isResizing = false;
59
+ document.removeEventListener('mousemove', handleMouseMove);
60
+ document.removeEventListener('mouseup', handleMouseUp);
61
+ };
62
+
63
+
64
+ // Query 1: Fetch the main workflow document
65
+ const { data: mainWorkflowData, isLoading: isLoadingMainWorkflow, error: errorMainWorkflow } = useQuery(
66
+ ['mainWorkflow', workflowId],
67
+ () => fetch('/api/data/search?_user='+me.username, {
68
+ method: 'POST',
69
+ headers: { 'Content-Type': 'application/json' },
70
+ body: JSON.stringify({
71
+ model: 'workflow',
72
+ filter: { $eq: [{$toString:'$_id'}, workflowId] },
73
+ // No pipelines, no depth here, as per user request
74
+ })
75
+ }).then(res => res.json()),
76
+ {
77
+ enabled: !!workflowId,
78
+ select: (data) => data?.data?.[0]
79
+ }
80
+ );
81
+
82
+ // Query 2: Fetch all workflowStep documents related to this workflow
83
+ const { data: workflowStepsData, isLoading: isLoadingSteps, error: errorSteps } = useQuery(
84
+ ['workflowSteps', workflowId],
85
+ () => fetch('/api/data/search?_user=' + me.username, {
86
+ method: 'POST',
87
+ headers: { 'Content-Type': 'application/json' },
88
+ body: JSON.stringify({
89
+ model: 'workflowStep',
90
+ filter: { workflow: workflowId },
91
+ // Use a shallow depth to populate direct relations like 'actions'
92
+ // This avoids a deep recursive lookup for the entire graph, but makes step data usable
93
+ depth: 1
94
+ })
95
+ }).then(res => res.json()),
96
+ {
97
+ enabled: !!workflowId, // La requête ne s'exécute que si workflowId est défini
98
+ select: (data) => data?.data || []
99
+ }
100
+ );
101
+
102
+ // Query 3: Fetch all workflowAction documents related to this workflow
103
+ const { data: workflowActionsData, isLoading: isLoadingActions, error: errorActions } = useQuery(
104
+ ['workflowActions', workflowId],
105
+ () => fetch('/api/data/search?_user=' + me.username, {
106
+ method: 'POST',
107
+ headers: { 'Content-Type': 'application/json' },
108
+ body: JSON.stringify({
109
+ model: 'workflowAction',
110
+ filter: { workflow: workflowId },
111
+ // No depth needed here as actions are usually flat
112
+ })
113
+ }).then(res => res.json()),
114
+ {
115
+ enabled: !!workflowId,
116
+ select: (data) => data?.data || []
117
+ }
118
+ );
119
+
120
+ useEffect(() => {
121
+ if (mainWorkflowData && workflowStepsData && workflowActionsData) {
122
+ const initialNodes = [];
123
+ const initialEdges = [];
124
+ const processedSteps = new Set();
125
+
126
+ // Create a map for quick lookup of steps
127
+ const stepsMap = new Map(workflowStepsData.map(step => [step._id, step]));
128
+
129
+ // 1. Nœud du déclencheur (Trigger)
130
+ initialNodes.push({
131
+ id: `workflow-start-${mainWorkflowData._id}`,
132
+ type: 'input',
133
+ position: { x: 250, y: 5 },
134
+ data: {
135
+ label: `Début: ${typeof mainWorkflowData.name === 'object' ? mainWorkflowData.name.value : mainWorkflowData.name}`,
136
+ // --- CORRECTION ---
137
+ // On inclut toutes les données du workflow pour la cohérence
138
+ ...mainWorkflowData
139
+ },
140
+ });
141
+
142
+ // Create a map for quick lookup of actions
143
+ const actionsMap = new Map(workflowActionsData.map(action => [action._id, action]));
144
+
145
+ // 2. Traitement récursif des étapes
146
+ const processStep = (stepId, parentNodeId, parentPosition, isSuccessBranch = true) => {
147
+ if (!stepId || processedSteps.has(stepId)) return;
148
+
149
+ const step = stepsMap.get(stepId);
150
+ if (!step) {
151
+ console.warn(`WorkflowEditor: Step with ID ${stepId} not found in fetched steps.`);
152
+ return;
153
+ }
154
+
155
+ processedSteps.add(stepId);
156
+
157
+ const nodePosition = { x: parentPosition.x, y: parentPosition.y + 120 };
158
+ initialNodes.push({
159
+ id: step._id,
160
+ type: 'default',
161
+ className: 'workflow-step-node',
162
+ position: nodePosition,
163
+ data: {
164
+ label: (step?.name && typeof step.name === 'object' ? step.name.value : step.name) || `Étape ${step._id}`,
165
+ ...step // --- CORRECTION --- On inclut l'objet step complet
166
+ },
167
+ });
168
+ initialEdges.push({ id: `e-${parentNodeId}-${step._id}`, source: parentNodeId, target: step._id, type: 'smoothstep' });
169
+
170
+ let lastActionId = step._id;
171
+ // The actions on the step are just IDs now, we need to look them up
172
+ const actionIds = Array.isArray(step.actions) ? step.actions : (step.actions ? [step.actions] : []);
173
+ const actions = actionIds.map(id => actionsMap.get(id)).filter(Boolean); // Get full action objects
174
+
175
+ if (actionIds.length !== actions.length) {
176
+ const missingIds = actionIds.filter(id => !actionsMap.has(id));
177
+ console.warn(`WorkflowEditor: Could not find the following actions: ${missingIds.join(', ')}`);
178
+ }
179
+
180
+ actions.forEach((action, index) => {
181
+ const actionNodePosition = { x: nodePosition.x, y: nodePosition.y + (index + 1) * 80 };
182
+ initialNodes.push({
183
+ id: action._id,
184
+ type: 'default',
185
+ className: 'workflow-action-node',
186
+ position: actionNodePosition,
187
+ data: {
188
+ label: action.name.value || `Action ${action._id}`,
189
+ ...action // --- CORRECTION --- On inclut l'objet action complet
190
+ },
191
+ });
192
+ initialEdges.push({ id: `e-${lastActionId}-${action._id}`, source: lastActionId, target: action._id, type: 'smoothstep' });
193
+ lastActionId = action._id;
194
+ });
195
+
196
+ // Créer les liens (edges)
197
+ if (step.onSuccessStep && typeof step.onSuccessStep === 'string') { // onSuccessStep is an ID
198
+ processStep(step.onSuccessStep, lastActionId, { x: nodePosition.x - 200, y: nodePosition.y + (actions.length * 80) }, true);
199
+ initialEdges.push({ id: `e-${lastActionId}-success-${step.onSuccessStep}`, source: lastActionId, target: step.onSuccessStep, label: t('onSuccess', 'onSuccess'), type: 'smoothstep' });
200
+ }
201
+
202
+ if (step.onFailureStep && typeof step.onFailureStep === 'string') { // onFailureStep is an ID
203
+ processStep(step.onFailureStep, lastActionId, { x: nodePosition.x + 200, y: nodePosition.y + (actions.length * 80) }, false);
204
+ initialEdges.push({ id: `e-${lastActionId}-failure-${step.onFailureStep}`, source: lastActionId, target: step.onFailureStep, label: t('onFailure', 'onFailure'), type: 'smoothstep', animated: true, style: { stroke: '#ff4d4d' } });
205
+ }
206
+ };
207
+
208
+ if (mainWorkflowData.startStep) {
209
+ initialEdges.push({
210
+ id: `e-start-${mainWorkflowData.startStep}`, // startStep is an ID
211
+ source: `workflow-start-${mainWorkflowData._id}`,
212
+ target: mainWorkflowData.startStep,
213
+ type: 'smoothstep',
214
+ });
215
+ processStep(mainWorkflowData.startStep, `workflow-start-${mainWorkflowData._id}`, { x: 250, y: 5 });
216
+ }
217
+
218
+ setNodes(initialNodes);
219
+ setEdges(initialEdges);
220
+ }
221
+ }, [mainWorkflowData, workflowStepsData, workflowActionsData, setNodes, setEdges, t]);
222
+
223
+ const onConnect = useCallback(
224
+ (params) => setEdges((eds) => addEdge(params, eds)),
225
+ [setEdges],
226
+ );
227
+
228
+ const onNodeClick = useCallback((event, node) => { // Keep this for potential future node editing
229
+ setSelectedNode(node);
230
+ console.log('Node clicked:', node);
231
+ }, []);
232
+
233
+ const onPaneClick = useCallback(() => { // Keep this for potential future node editing
234
+ setSelectedNode(null);
235
+ }, []);
236
+
237
+ if (isLoadingMainWorkflow || isLoadingSteps || isLoadingActions) { // Check all loading states
238
+ return <div>{t('loading', 'Chargement...')}</div>;
239
+ }
240
+
241
+ if (errorMainWorkflow || errorSteps || errorActions) { // Check all error states
242
+ const errorMessage = errorMainWorkflow?.message || errorSteps?.message || errorActions?.message || 'Unknown error';
243
+ return <div><Trans i18nKey="error.generic" values={{ message: errorMessage }}>Erreur: {{ message: errorMessage }}</Trans></div>;
244
+ }
245
+
246
+ return (
247
+ <div className={"flex-1"} style={{ width: '100%', height: '80vh', display: 'flex' }}>
248
+ <ReactFlow
249
+ nodes={nodes}
250
+ edges={edges}
251
+ onNodesChange={onNodesChange}
252
+ onEdgesChange={onEdgesChange}
253
+ onConnect={onConnect}
254
+ onNodeClick={onNodeClick}
255
+ onPaneClick={onPaneClick}
256
+ nodeTypes={nodeTypes}
257
+ fitView
258
+ >
259
+ <Controls />
260
+ <MiniMap />
261
+ <Background variant="dots" gap={12} size={1} />
262
+ </ReactFlow>
263
+
264
+ {/* Panneau latéral pour l'édition des propriétés */}
265
+ {selectedNode && (
266
+ <div className="properties-panel" style={{ width: `${panelWidth}px`, padding: '10px', borderLeft: '1px solid #ccc' }}>
267
+ <div
268
+ className="resizer"
269
+ onMouseDown={handleMouseDown}
270
+ />
271
+ <h3><Trans i18nKey="properties">Propriétés</Trans></h3>
272
+ <p><strong>ID:</strong> {selectedNode.id}</p>
273
+
274
+ {/* Affiche les conditions pour un workflowStep */}
275
+ {selectedNode.data.conditions && (
276
+ <div className="mt-2">
277
+ <strong><Trans i18nKey="conditions">Conditions:</Trans></strong>
278
+ <CodeField language="json" value={JSON.stringify(selectedNode.data.conditions, null, 2)} readOnly={true} />
279
+ </div>
280
+ )}
281
+
282
+ {/* Affiche le script pour une action ExecuteScript */}
283
+ {selectedNode.data.type && (
284
+ <div className="mt-2">
285
+ <strong><Trans i18nKey="action">Action :</Trans></strong> {selectedNode.data.type}
286
+ {selectedNode.data.targetModel&& (
287
+ <p>
288
+ <strong><Trans i18nKey="action">Modèle cible : </Trans></strong>{selectedNode.data.targetModel}
289
+ </p>
290
+ )}
291
+ {selectedNode.data.targetSelector&& (
292
+ <p>
293
+ <strong><Trans i18nKey="filter">Filtre sur :</Trans></strong>
294
+ <br/><CodeField language="javascript" value={JSON.stringify(selectedNode.data.targetSelector)} readOnly={true} />
295
+ </p>
296
+ )}
297
+ {(selectedNode.data.dataToCreate || selectedNode.data.fieldsToUpdate) && (
298
+ <p>
299
+ <strong><Trans i18nKey="filter">Code déclenché :</Trans></strong>
300
+ <br/><CodeField language="javascript" value={JSON.stringify(selectedNode.data.dataToCreate || selectedNode.data.fieldsToUpdate)} readOnly={true} />
301
+ </p>
302
+ )}
303
+ </div>
304
+ )}
305
+ {/* Affiche le script pour une action ExecuteScript */}
306
+ {selectedNode.data.type === 'ExecuteScript' && selectedNode.data.script && (
307
+ <div className="mt-2">
308
+ <CodeField language="javascript" value={selectedNode.data.script} readOnly={true} />
309
+ </div>
310
+ )}
311
+ </div>
312
+ )}
313
+ </div>
314
+ );
315
+ };
316
+
317
+ export default WorkflowEditor;
@@ -0,0 +1,16 @@
1
+
2
+ .workflow-step-node {
3
+ background-color: #e0e7ff; /* Un bleu clair pour les étapes */
4
+ border-color: #a5b4fc;
5
+ border-width: 2px;
6
+ font-weight: bold;
7
+ }
8
+
9
+ .workflow-action-node {
10
+ background-color: #f0f9ff; /* Un bleu très clair pour les actions */
11
+ border-color: #bae6fd;
12
+ border-width: 1px;
13
+ border-style: dashed;
14
+ width: auto;
15
+ min-width: 150px;
16
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "data-primals-engine",
3
- "version": "1.6.1",
3
+ "version": "1.6.2",
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",
@@ -33,7 +33,9 @@
33
33
  "prismjs": "1.30.0",
34
34
  "xml2js": ">=0.5.0",
35
35
  "tar-fs": ">=3.1.1",
36
- "vite": ">=7.0.8"
36
+ "vite": ">=7.0.8",
37
+ "node-forge": ">=1.3.2",
38
+ "js-yaml": ">=4.1.1"
37
39
  },
38
40
  "overrides": {
39
41
  "react-syntax-highlighter": {
@@ -52,17 +54,18 @@
52
54
  },
53
55
  "exports": {
54
56
  ".": "./src/index.js",
57
+ "./client-only": "./src/client.js",
55
58
  "./modules/*.js": "./src/modules/*.js",
56
59
  "./modules/*": "./src/modules/*/index.js",
57
60
  "./client": "./client/index.js",
58
61
  "./*": "./src/*.js"
59
62
  },
60
63
  "dependencies": {
61
- "@langchain/core": "^0.3.66",
64
+ "@langchain/core": "^0.3.79",
62
65
  "archiver": "^7.0.1",
63
66
  "aws-sdk": "^2.1692.0",
64
67
  "bcrypt": "^6.0.0",
65
- "body-parser": "^2.2.0",
68
+ "body-parser": "^2.2.1",
66
69
  "chalk": "^5.4.1",
67
70
  "check-disk-space": "^3.4.0",
68
71
  "compression": "^1.8.1",
@@ -77,44 +80,44 @@
77
80
  "express-session": "^1.18.2",
78
81
  "handlebars": "^4.7.8",
79
82
  "i18next-browser-languagedetector": "^8.2.0",
80
- "isolated-vm": "^5.0.0",
81
- "juice": "^11.0.1",
82
- "mathjs": "^14.6.0",
83
+ "isolated-vm": "^5.0.4",
84
+ "juice": "^11.0.3",
85
+ "mathjs": "^15.1.0",
83
86
  "mongodb": "^6.18.0",
84
87
  "node-cache": "^5.1.2",
85
88
  "node-schedule": "^2.1.1",
86
- "nodemailer": "^7.0.7",
87
- "openai": "^5.10.2",
89
+ "nodemailer": "^7.0.10",
90
+ "openai": "^6.9.1",
88
91
  "passport": "^0.7.0",
89
92
  "passport-saml-encrypted": "^0.1.13",
90
93
  "process": "^0.11.10",
91
94
  "prop-types": "^15.8.1",
92
95
  "randomcolor": "^0.6.2",
93
96
  "react-markdown": "^10.1.0",
94
- "read-excel-file": "^5.8.8",
97
+ "read-excel-file": "^6.0.1",
95
98
  "request-ip": "^3.3.0",
96
99
  "safe-regex": "^2.1.1",
97
100
  "sanitize-html": "^2.17.0",
98
- "sirv": "^3.0.1",
99
- "stripe": "^18.4.0",
101
+ "sirv": "^3.0.2",
102
+ "stripe": "^20.0.0",
100
103
  "swagger-ui-express": "^5.0.1",
101
- "tar": "^7.4.3",
104
+ "tar": "^7.5.2",
102
105
  "tinycolor2": "^1.6.0",
103
106
  "uniqid": "^5.4.0",
104
107
  "vitest": "^3.2.4",
105
- "yaml": "^2.8.0"
108
+ "yaml": "^2.8.1"
106
109
  },
107
110
  "peerDependencies": {
108
111
  "express": "^5.1.0",
109
112
  "passport-azure-ad": "^4.3.5",
110
113
  "passport-google-oauth20": "^2.0.0",
111
114
  "react": "18.3.1",
112
- "react-i18next": "^15.6.1",
113
- "react-query": ">=3.0.0",
114
- "@langchain/anthropic": "^0.3.26",
115
+ "react-i18next": "^16.3.4",
116
+ "react-query": "^3.39.3",
117
+ "@langchain/anthropic": "^0.3.33",
115
118
  "@langchain/deepseek": "^0.1.0",
116
- "@langchain/google-genai": "^0.2.16",
117
- "@langchain/openai": "^0.6.3"
119
+ "@langchain/google-genai": "^0.2.18",
120
+ "@langchain/openai": "^0.6.16"
118
121
  },
119
122
  "devDependencies": {
120
123
  "@eslint/js": "^9.32.0",
package/src/client.js ADDED
@@ -0,0 +1,4 @@
1
+ // data-primals-engine/client
2
+ // Ce fichier sert de point d'entrée pour les modules qui peuvent être utilisés en toute sécurité côté client (navigateur).
3
+
4
+ export { Event } from './events.js';
@@ -918,7 +918,8 @@ export const defaultModels = {
918
918
  "tags": ["system", "automation"],
919
919
  fields: [
920
920
  { name: 'name', type: 'string_t', required: true, hint: "Unique name for the workflow (e.g., 'Order Validation', 'Low Stock Notification')." },
921
- { name: 'description', type: 'richtext', hint: "Detailed explanation of the workflow's purpose." },
921
+ { name: 'description', type: 'richtext', hint: "Detailed explanation of the workflow's purpose."
922
+ },
922
923
  { name: 'startStep', type: 'relation', relation: 'workflowStep', required: false, hint: "The first step to execute when the workflow starts." }
923
924
  ]
924
925
  },
@@ -982,7 +983,8 @@ export const defaultModels = {
982
983
  "tags": ["system", "automation"],
983
984
  fields: [
984
985
  { name: 'workflow', type: 'relation', relation: 'workflow', required: true, hint: "The workflow this step belongs to." },
985
- { name: 'name', type: 'string_t', hint: "Optional descriptive name for the step (e.g., 'Check Inventory', 'Send Confirmation Email')." },
986
+ { name: 'name', type: 'string_t', hint: "Optional descriptive name for the step (e.g., 'Check Inventory', 'Send Confirmation Email')."
987
+ },
986
988
  { name: 'conditions', type: 'code', language: 'json', conditionBuilder: true, hint: "Optional conditions checked before executing the step's action." },
987
989
  { name: 'actions', type: 'relation', relation: 'workflowAction', multiple: true, required: true, hint: "The main actions performed by this step." },
988
990
  { name: 'onSuccessStep', type: 'relation', relation: 'workflowStep', hint: "Optional: The next step if this step's action succeeds." },
@@ -997,6 +999,7 @@ export const defaultModels = {
997
999
  "tags": ["system", "automation"],
998
1000
  fields: [
999
1001
  { name: 'name', type: 'string_t', required: true, hint: "Name of the action (e.g., 'Update Order Status', 'Send Email', 'Call Payment API')." },
1002
+ { name: 'workflow', type: 'relation', relation: 'workflow', required: false },
1000
1003
  {
1001
1004
  name: 'type',
1002
1005
  type: 'enum',
package/src/i18n.js CHANGED
@@ -5,6 +5,10 @@ import LanguageDetector from "i18next-browser-languagedetector";
5
5
  export const translations = {
6
6
  fr: {
7
7
  translation: {
8
+
9
+ "model_audience": "Audiences",
10
+ "model_deal": "Opportunités",
11
+
8
12
  "relationField.select.title": "Sélectionner {{modelName}}",
9
13
  "btns.validate": "Valider",
10
14
  "btns.create": "Créer",
@@ -1318,6 +1322,8 @@ export const translations = {
1318
1322
  },
1319
1323
  en: {
1320
1324
  translation: {
1325
+ "model_audience": "Audiences",
1326
+ "model_deal": "Opportunities",
1321
1327
  "relationField.select.title": "Select {{modelName}}",
1322
1328
  "btns.validate": "Validate",
1323
1329
  "btns.create": "Create",
@@ -2627,6 +2633,8 @@ export const translations = {
2627
2633
  },
2628
2634
  es: {
2629
2635
  translation: {
2636
+ "model_audience": "Audiencias",
2637
+ "model_deal": "Oportunidades",
2630
2638
  "relationField.select.title": "Seleccionar {{modelName}}",
2631
2639
  "btns.validate": "Validar",
2632
2640
  "btns.create": "Crear",
@@ -3973,6 +3981,10 @@ export const translations = {
3973
3981
  },
3974
3982
  pt: {
3975
3983
  translation: {
3984
+
3985
+ "model_audience": "Públicos",
3986
+ "model_campaign": "Campanhas de marketing",
3987
+ "model_deal": "Oportunidades",
3976
3988
  "relationField.select.title": "Selecionar {{modelName}}",
3977
3989
  "btns.validate": "Validar",
3978
3990
  "btns.create": "Criar",
@@ -4125,6 +4137,8 @@ export const translations = {
4125
4137
  },
4126
4138
  de: {
4127
4139
  translation: {
4140
+ "model_audience": "Zielgruppen",
4141
+ "model_deal": "Möglichkeiten",
4128
4142
  "relationField.select.title": "{{modelName}} auswählen",
4129
4143
  "btns.validate": "Validieren",
4130
4144
  "btns.create": "Erstellen",
@@ -5424,7 +5438,6 @@ export const translations = {
5424
5438
  "field_endpoint_isPublic_hint": "Se selezionato, questo endpoint sarà accessibile senza autenticazione. Lo script verrà eseguito con i permessi del proprietario dell'endpoint.",
5425
5439
  "field_endpoint_code": "Codice da eseguire",
5426
5440
  "field_endpoint_code_hint": "Lo script da eseguire. Deve restituire un valore o un oggetto che sarà la risposta JSON.",
5427
- "model_env": "Variabili d'Ambiente (env)",
5428
5441
  "datatable.advancedFilter.title": "Filtro avanzato (MongoDB)",
5429
5442
  "datatable.advancedFilter.desc": "Crea i tuoi filtri avanzati da zero, utilizzando gli operatori di aggregazione di MongoDB disponibili qui.",
5430
5443
  "editData": "Modifica in {{0}}",
@@ -5696,6 +5709,10 @@ export const translations = {
5696
5709
  "model_channel": "Canali di notifica",
5697
5710
  "model_message": "Messaggi",
5698
5711
  "model_kpi": "Indicatori KPI",
5712
+ "model_env": "Variabili d'Ambiente (env)",
5713
+ "model_audience": "Pubblici",
5714
+ "model_deal": "Opportunità",
5715
+
5699
5716
  "field_permission_name": "Nome",
5700
5717
  "field_permission_description": "Descrizione",
5701
5718
 
@@ -6628,6 +6645,9 @@ export const translations = {
6628
6645
  },
6629
6646
  cs: {
6630
6647
  translation: {
6648
+ "model_audience": "Publika",
6649
+ "model_campaign": "Marketingové kampaně",
6650
+ "model_deal": "Příležitosti",
6631
6651
  "relationField.select.title": "Vybrat {{modelName}}",
6632
6652
  "btns.validate": "Ověřit",
6633
6653
  "btns.create": "Vytvořit",
@@ -7553,6 +7573,8 @@ export const translations = {
7553
7573
  },
7554
7574
  ru: {
7555
7575
  translation: {
7576
+ "model_audience": "Аудитории",
7577
+ "model_deal": "Возможности",
7556
7578
  "relationField.select.title": "Выбрать {{modelName}}",
7557
7579
  "btns.validate": "Проверить",
7558
7580
  "btns.create": "Создать",
@@ -8832,6 +8854,8 @@ export const translations = {
8832
8854
  },
8833
8855
  ar: {
8834
8856
  translation: {
8857
+ "model_audience": "الجماهير",
8858
+ "model_deal": "الفرص",
8835
8859
  "relationField.select.title": "اختر {{modelName}}",
8836
8860
  "btns.validate": "تحقق",
8837
8861
  "btns.create": "إنشاء",
@@ -8901,7 +8925,7 @@ export const translations = {
8901
8925
  "field_endpoint_code": "الكود المنفذ",
8902
8926
  "field_endpoint_code_hint": "السكريبت الذي سيتم تنفيذه. يجب أن يعيد قيمة أو كائنًا سيكون استجابة JSON.",
8903
8927
  "model_env": "متغيرات البيئة (env)",
8904
-
8928
+
8905
8929
  "datatable.advancedFilter.title": "مرشح متقدم (MongoDB)",
8906
8930
  "datatable.advancedFilter.desc": "أنشئ مرشحاتك المتقدمة من الصفر، باستخدام عوامل تجميع MongoDB المتوفرة هنا",
8907
8931
  "editData": "تعديل في {{0}}",
@@ -10156,6 +10180,8 @@ export const translations = {
10156
10180
  },
10157
10181
  sv: {
10158
10182
  translation: {
10183
+ "model_audience": "Målgrupper",
10184
+ "model_deal": "Möjligheter",
10159
10185
  "relationField.select.title": "Välj {{modelName}}",
10160
10186
  "btns.validate": "Validera",
10161
10187
  "btns.create": "Skapa",
@@ -11316,7 +11342,7 @@ export const translations = {
11316
11342
  "no": "Nej",
11317
11343
  "true": "Sant",
11318
11344
  "false": "Falskt",
11319
-
11345
+
11320
11346
 
11321
11347
  "assistant.type": "Skriv ditt meddelande...",
11322
11348
  "assistant.welcome": "Hej! Hur kan jag hjälpa dig med din data?",
@@ -11383,6 +11409,10 @@ export const translations = {
11383
11409
  },
11384
11410
  el: {
11385
11411
  translation: {
11412
+ "model_audience": "Κοινά",
11413
+ "model_deal": "Ευκαιρίες",
11414
+ "model_endpoint": "Τελικά σημεία",
11415
+ "model_env": "Μεταβλητές Περιβάλλοντος",
11386
11416
  "relationField.select.title": "Επιλογή {{modelName}}",
11387
11417
  "btns.validate": "Επικύρωση",
11388
11418
  "btns.create": "Δημιουργία",
@@ -12586,6 +12616,8 @@ export const translations = {
12586
12616
  },
12587
12617
  fa: {
12588
12618
  translation: {
12619
+ "model_audience": "مخاطبان",
12620
+ "model_deal": "فرصت‌ها",
12589
12621
  "relationField.select.title": "{{modelName}} را انتخاب کنید",
12590
12622
  "btns.validate": "اعتبارسنجی",
12591
12623
  "btns.create": "ایجاد",