data-primals-engine 1.2.6-rc3 → 1.3.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.
Files changed (40) hide show
  1. package/README.md +38 -2
  2. package/client/package-lock.json +247 -4354
  3. package/client/package.json +16 -15
  4. package/client/src/App.jsx +13 -20
  5. package/client/src/App.scss +1 -0
  6. package/client/src/AssistantChat.jsx +5 -4
  7. package/client/src/DataEditor.jsx +2 -2
  8. package/client/src/DataTable.jsx +47 -3
  9. package/client/src/ExportDialog.jsx +2 -2
  10. package/client/src/Field.jsx +6 -18
  11. package/client/src/KanbanCard.jsx +4 -2
  12. package/client/src/KanbanConfigModal.jsx +5 -7
  13. package/client/src/ModelCreatorField.jsx +9 -9
  14. package/client/src/PackGallery.jsx +89 -9
  15. package/client/src/PackGallery.scss +58 -4
  16. package/client/src/RTETrans.jsx +11 -0
  17. package/client/src/RelationValue.jsx +3 -4
  18. package/client/src/constants.js +1 -1
  19. package/client/src/core/data.js +2 -1
  20. package/client/src/translations.js +80 -0
  21. package/package.json +8 -1
  22. package/server.js +4 -4
  23. package/src/constants.js +6 -0
  24. package/src/defaultModels.js +23 -10
  25. package/src/filter.js +35 -5
  26. package/src/i18n.js +1 -1
  27. package/src/modules/{assistant.js → assistant/assistant.js} +42 -27
  28. package/src/modules/assistant/constants.js +16 -0
  29. package/src/modules/data/data.core.js +1 -3
  30. package/src/modules/data/data.js +4601 -4525
  31. package/src/modules/data/data.routes.js +29 -3
  32. package/src/modules/mongodb.js +3 -1
  33. package/src/modules/user.js +12 -1
  34. package/src/modules/workflow.js +198 -117
  35. package/src/packs.js +1015 -9
  36. package/src/services/index.js +11 -0
  37. package/src/services/stripe.js +141 -0
  38. package/test/data.integration.test.js +66 -3
  39. package/test/workflow.actions.integration.test.js +474 -0
  40. package/test/workflow.integration.test.js +1 -1
@@ -7,20 +7,38 @@
7
7
 
8
8
  .gallery-header {
9
9
  display: flex;
10
+ flex-wrap: wrap;
11
+ gap: 1rem ;
10
12
  justify-content: space-between;
11
13
  align-items: center;
12
14
  padding-bottom: 1rem;
13
15
  border-bottom: 1px solid #e0e0e0;
14
16
  margin-bottom: 1rem;
17
+
15
18
  h1 {
16
19
  margin: 0;
20
+ flex: 1; // Permet au titre de prendre l'espace disponible
21
+ min-width: 200px; // Empêche le titre de devenir trop petit
17
22
  }
18
- .sort-options {
23
+
24
+ .header-actions {
19
25
  display: flex;
20
- gap: 0.5rem;
26
+ gap: 1rem;
21
27
  align-items: center;
22
- .btn.active {
23
- color: white;
28
+
29
+ .sort-options {
30
+ display: flex;
31
+ gap: 0.5rem;
32
+ align-items: center;
33
+ flex-wrap: wrap; // Pour le responsive
34
+
35
+ .btn.active {
36
+ color: white;
37
+ }
38
+ }
39
+
40
+ .add-pack-button {
41
+ white-space: nowrap; // Empêche le texte du bouton de se casser
24
42
  }
25
43
  }
26
44
  }
@@ -153,4 +171,40 @@
153
171
  }
154
172
  }
155
173
  }
174
+ }
175
+
176
+ // À ajouter à la fin de votre fichier CSS
177
+ .manual-install-dialog {
178
+ width: 100%;
179
+
180
+ p {
181
+ margin-top: 0;
182
+ color: #666;
183
+ font-size: 0.9em;
184
+ }
185
+
186
+ textarea {
187
+ width: 100%;
188
+ padding: 1rem;
189
+ border: 1px solid #ddd;
190
+ border-radius: 4px;
191
+ font-family: monospace;
192
+ resize: vertical;
193
+ min-height: 300px;
194
+ background-color: #f9f9f9;
195
+ color: #333;
196
+
197
+ &:focus {
198
+ outline: none;
199
+ border-color: var(--primary-color);
200
+ box-shadow: 0 0 0 2px rgba(var(--primary-color-rgb), 0.2);
201
+ }
202
+ }
203
+
204
+ .dialog-actions {
205
+ display: flex;
206
+ justify-content: flex-end;
207
+ gap: 0.5rem;
208
+ margin-top: 1rem;
209
+ }
156
210
  }
@@ -53,6 +53,16 @@ const RTETrans = ({ value, onChange, field }) => {
53
53
  };
54
54
  const languagesToAdd = availableLangs.filter(lang => !existingLangs.includes(lang.code));
55
55
 
56
+ const confirmDelete = (e, lang) => {
57
+ e.preventDefault();
58
+ e.stopPropagation();
59
+ if( !confirm(t('rte.confirmLangDeletion', 'Confirm lang deletion ?')))
60
+ return;
61
+ const val = {...value};
62
+ delete val[lang];
63
+ onChange(val);
64
+ setActiveTab(Object.keys(val)[0] || null);
65
+ }
56
66
  return (
57
67
  <div className="rte-trans-container">
58
68
  <div className="tabs-container flex items-center border-b border-gray-200">
@@ -64,6 +74,7 @@ const RTETrans = ({ value, onChange, field }) => {
64
74
  title={availableLangs.find(f=>f.code===lang)?.name.value || ''}
65
75
  >
66
76
  {lang.toUpperCase()}
77
+ <button onClick={(e) => confirmDelete(e, lang)}>x</button>
67
78
  </button>
68
79
  ))}
69
80
  {languagesToAdd.length > 0 && (
@@ -187,8 +187,7 @@ const RelationValue = ({ field, data, align }) => {
187
187
  if (f.type === 'code') {
188
188
  let t;
189
189
  try {
190
- t = f.language === 'json' ? JSON.stringify(intVal[f.name], null, 2) : '';
191
-
190
+ t = f.language === 'json' ? JSON.stringify(intVal[f.name], null, 2) : t = intVal[f.name].toString();
192
191
  } catch (e) {
193
192
  t = intVal[f.name].toString();
194
193
  }
@@ -196,8 +195,8 @@ const RelationValue = ({ field, data, align }) => {
196
195
  <dt>{columnName} {span}</dt>
197
196
  <dd>
198
197
  <CodeField onChange={() => {
199
- }} language={field.language || 'json'}
200
- name={f.name} value={field.language === 'json' ? JSON.stringify(intVal[f.name], null, 2) : t} />
198
+ }} language={f.language || 'json'}
199
+ name={f.name} value={f.language === 'json' ? JSON.stringify(intVal[f.name], null, 2) : t} />
201
200
  </dd>
202
201
  </>;
203
202
  }
@@ -65,7 +65,7 @@ export const OPERAND_TYPES = {
65
65
 
66
66
 
67
67
  export const getHost = () => {
68
- return process.env.HOST || host || 'localhost';
68
+ return import.meta.env.HOST || host || 'localhost';
69
69
  }
70
70
 
71
71
 
@@ -1,4 +1,5 @@
1
- import {getHost, MONGO_OPERATORS} from "../constants.js";
1
+ import { MONGO_OPERATORS} from "../constants.js";
2
+ import {getHost} from "../../../src/constants";
2
3
 
3
4
  const isProd = import.meta.env.MODE === 'production';
4
5
  export const urlData = isProd ? 'https://'+getHost()+'/' : 'http://localhost:7633/';
@@ -2,6 +2,17 @@
2
2
  export const websiteTranslations = {
3
3
  fr: {
4
4
  translation: {
5
+
6
+ "field_endpoint_isPublic": "Accès public",
7
+ "field_endpoint_isPublic_hint": "Si coché, ce point d'accès sera accessible sans authentification. Le script s'exécutera avec les droits du propriétaire du point d'accès.",
8
+
9
+ "packs.manualInstall": "Importer",
10
+ "packs.manualInstall.title": "Installation manuelle de pack",
11
+ "packs.manualInstall.instructions": "Collez ici le JSON de configuration du pack que vous souhaitez installer.",
12
+ "packs.manualInstall.placeholder": "{\"name\": \"Mon Pack\", \"description\": \"...\", \"models\": [...], \"data\": [...]}",
13
+
14
+ "api.data.relationFilterFailed": "La valeur {{value}} pour le champ {{field}} ne respecte pas le filtre de relation défini.",
15
+
5
16
  "dataimporter.excelPreview":"Aperçu des données Excel",
6
17
  "dataimporter.previewNote": "Note: Ceci est un aperçu des premières lignes. Les cellules vides sont affichées comme \"(vide)\".",
7
18
  "dataimporter.nullValue": "(vide)",
@@ -532,6 +543,11 @@ export const websiteTranslations = {
532
543
  },
533
544
  en: {
534
545
  translation: {
546
+ "packs.manualInstall": "Import",
547
+ "packs.manualInstall.title": "Manual Pack Installation",
548
+ "packs.manualInstall.instructions": "Paste the configuration JSON of the pack you want to install here.",
549
+ "packs.manualInstall.placeholder": "{\"name\": \"My Pack\", \"description\": \"...\", \"models\": [...], \"data\": [...]}",
550
+
535
551
  "dataimporter.excelPreview": "Excel Data Preview",
536
552
  "dataimporter.previewNote": "Note: This is a preview of the first few rows. Empty cells are displayed as \"(empty)\".",
537
553
  "dataimporter.nullValue": "(empty)",
@@ -567,6 +583,8 @@ export const websiteTranslations = {
567
583
  "suggested_prompt.recipes.title": "Cooking Recipes",
568
584
  "suggested_prompt.recipes.prompt": "A collection of cooking recipes, with ingredients, instructions, and preparation time.",
569
585
 
586
+ "api.data.relationFilterFailed": "The value {{value}} for field {{field}} does not match the defined relationship filter.",
587
+
570
588
  "api.data.duplicateValue": "The value '{{value}}' already exists for the unique field '{{field}}'.",
571
589
  "api.model.invalidStructure": "The model is invalid. Some fields are incorrect.",
572
590
  "api.validate.fieldArray": "Field '{{0}}' must be an array.",
@@ -1939,6 +1957,11 @@ export const websiteTranslations = {
1939
1957
  },
1940
1958
  es: {
1941
1959
  translation: {
1960
+ "packs.manualInstall": "Importar",
1961
+ "packs.manualInstall.title": "Instalación manual del paquete",
1962
+ "packs.manualInstall.instructions": "Pegue aquí el JSON de configuración del paquete que desea instalar.",
1963
+ "packs.manualInstall.placeholder": "{\"name\": \"Mi paquete\", \"description\": \"...\", \"models\": [...], \"data\": [...]}",
1964
+
1942
1965
  "dataimporter.excelPreview": "Vista previa de datos de Excel",
1943
1966
  "dataimporter.previewNote": "Nota: Esta es una vista previa de las primeras filas. Las celdas vacías se muestran como \"(vacío)\".",
1944
1967
  "dataimporter.nullValue": "(vacío)",
@@ -1975,6 +1998,7 @@ export const websiteTranslations = {
1975
1998
  "suggested_prompt.recipes.prompt": "Una colección de recetas de cocina, con ingredientes, instrucciones y tiempo de preparación.",
1976
1999
 
1977
2000
  "api.data.duplicateValue": "El valor '{{value}}' ya existe para el campo único '{{field}}'.",
2001
+ "api.data.relationFilterFailed": "El valor {{value}} del campo {{field}} no coincide con el filtro de relación definido.",
1978
2002
  "api.model.invalidStructure": "El modelo no es válido. Algunos campos son incorrectos.",
1979
2003
  "api.validate.fieldArray": "El campo '{{0}}' debe ser un array.",
1980
2004
  "api.validate.invalidMimeType": "Tipo de archivo '{{type}}' no válido. Los tipos permitidos son: {{authorized}}.",
@@ -3351,6 +3375,11 @@ export const websiteTranslations = {
3351
3375
  },
3352
3376
  pt: {
3353
3377
  translation: {
3378
+ "packs.manualInstall": "Importar",
3379
+ "packs.manualInstall.title": "Instalação manual do pacote",
3380
+ "packs.manualInstall.instructions": "Cole o JSON de configuração do pacote que você deseja instalar aqui.",
3381
+ "packs.manualInstall.placeholder": "{\"name\": \"Meu pacote\", \"description\": \"...\", \"models\": [...], \"data\": [...]}",
3382
+
3354
3383
  "dataimporter.excelPreview": "Visualização de Dados do Excel",
3355
3384
  "dataimporter.previewNote": "Observação: Esta é uma visualização das primeiras linhas. Células vazias são exibidas como \"(vazio)\".",
3356
3385
  "dataimporter.nullValue": "(vazio)",
@@ -3388,6 +3417,7 @@ export const websiteTranslations = {
3388
3417
  "suggested_prompt.recipes.prompt": "Uma coleção de receitas culinárias, com ingredientes, instruções e tempo de preparação.",
3389
3418
 
3390
3419
  "api.data.duplicateValue": "O valor '{{value}}' já existe para o campo exclusivo '{{field}}'.",
3420
+ "api.data.relationFilterFailed": "O valor {{value}} para o campo {{field}} não corresponde ao filtro de relacionamento definido.",
3391
3421
  "api.model.invalidStructure": "O modelo é inválido. Alguns campos estão incorretos.",
3392
3422
  "api.validate.fieldArray": "O campo '{{0}}' deve ser um array.",
3393
3423
  "api.validate.invalidMimeType": "Tipo de ficheiro inválido '{{type}}'. Os tipos permitidos são: {{authorized}}.",
@@ -4758,6 +4788,11 @@ export const websiteTranslations = {
4758
4788
  },
4759
4789
  de: {
4760
4790
  translation: {
4791
+ "packs.manualInstall": "Importieren",
4792
+ "packs.manualInstall.title": "Manuelle Paketinstallation",
4793
+ "packs.manualInstall.instructions": "Fügen Sie hier die JSON-Konfiguration des zu installierenden Pakets ein.",
4794
+ "packs.manualInstall.placeholder": "{\"name\": \"Mein Paket\", \"description\": \"...\", \"models\": [...], \"data\": [...]}",
4795
+
4761
4796
  "dataimporter.excelPreview": "Excel-Datenvorschau",
4762
4797
  "dataimporter.previewNote": "Hinweis: Dies ist eine Vorschau der ersten Zeilen. Leere Zellen werden als \"(leer)\" angezeigt.",
4763
4798
  "dataimporter.nullValue": "(leer)",
@@ -4782,6 +4817,7 @@ export const websiteTranslations = {
4782
4817
  "suggested_prompt.recipes.prompt": "Eine Sammlung von Kochrezepten mit Zutaten, Anweisungen und Vorbereitungszeit.",
4783
4818
 
4784
4819
  "api.data.duplicateValue": "Der Wert '{{value}}' ist für das eindeutige Feld '{{field}}' bereits vorhanden.",
4820
+ "api.data.relationFilterFailed": "Der Wert {{value}} für das Feld {{field}} entspricht nicht dem definierten Beziehungsfilter.",
4785
4821
  "api.model.invalidStructure": "Das Modell ist ungültig. Einige Felder sind fehlerhaft.",
4786
4822
  "api.validate.fieldArray": "Feld '{{0}}' muss ein Array sein.",
4787
4823
  "api.validate.invalidMimeType": "Ungültiger Dateityp '{{type}}'. Zulässige Typen sind: {{authorized}}.",
@@ -6149,6 +6185,11 @@ export const websiteTranslations = {
6149
6185
  },
6150
6186
  it: {
6151
6187
  translation: {
6188
+ "packs.manualInstall": "Importa",
6189
+ "packs.manualInstall.title": "Installazione manuale del pacchetto",
6190
+ "packs.manualInstall.instructions": "Incolla qui il file JSON di configurazione del pacchetto che desideri installare.",
6191
+ "packs.manualInstall.placeholder": "{\"name\": \"My Pack\", \"description\": \"...\", \"models\": [...], \"data\": [...]}",
6192
+
6152
6193
  "dataimporter.excelPreview": "Anteprima dati Excel",
6153
6194
  "dataimporter.previewNote": "Nota: questa è un'anteprima delle prime righe. Le celle vuote vengono visualizzate come \"(vuoto)\".",
6154
6195
  "dataimporter.nullValue": "(vuoto)",
@@ -6185,6 +6226,7 @@ export const websiteTranslations = {
6185
6226
  "suggested_prompt.recipes.prompt": "Una raccolta di ricette di cucina, con ingredienti, istruzioni e tempi di preparazione.",
6186
6227
 
6187
6228
  "api.data.duplicateValue": "Il valore '{{value}}' esiste già per il campo univoco '{{field}}'.",
6229
+ "api.data.relationFilterFailed": "Il valore {{value}} per il campo {{field}} non corrisponde al filtro di relazione definito.",
6188
6230
  "api.model.invalidStructure": "Il modello non è valido. Alcuni campi sono errati.",
6189
6231
  "api.validate.fieldArray": "Il campo '{{0}}' deve essere un array.",
6190
6232
  "api.validate.invalidMimeType": "Tipo di file '{{type}}' non valido. I tipi consentiti sono: {{authorized}}.",
@@ -7553,6 +7595,11 @@ export const websiteTranslations = {
7553
7595
  },
7554
7596
  cs: {
7555
7597
  translation: {
7598
+ "packs.manualInstall": "Importovat",
7599
+ "packs.manualInstall.title": "Ruční instalace balíčku",
7600
+ "packs.manualInstall.instructions": "Sem vložte konfigurační JSON balíčku, který chcete nainstalovat.",
7601
+ "packs.manualInstall.placeholder": "{\"name\": \"Můj balíček\", \"description\": \"...\", \"models\": [...], \"data\": [...]}",
7602
+
7556
7603
  "dataimporter.excelPreview": "Náhled dat v Excelu",
7557
7604
  "dataimporter.previewNote": "Poznámka: Toto je náhled prvních několika řádků. Prázdné buňky se zobrazují jako \"(prázdné)\".",
7558
7605
  "dataimporter.nullValue": "(prázdné)",
@@ -7589,6 +7636,7 @@ export const websiteTranslations = {
7589
7636
  "suggested_prompt.recipes.prompt": "Sbírka receptů na vaření s ingrediencemi, návodem a přípravou čas.",
7590
7637
 
7591
7638
  "api.data.duplicateValue": "Hodnota '{{value}}' pro jedinečné pole '{{field}}' již existuje.",
7639
+ "api.data.relationFilterFailed": "Hodnota {{value}} pro pole {{field}} neodpovídá definovanému filtru vztahů.",
7592
7640
  "api.model.invalidStructure": "Model je neplatný. Některá pole jsou nesprávná.",
7593
7641
  "api.validate.fieldArray": "Pole '{{0}}' musí být pole.",
7594
7642
  "api.validate.invalidMimeType": "Neplatný typ souboru '{{type}}'. Povolené typy jsou: {{authorized}}.",
@@ -8952,6 +9000,11 @@ export const websiteTranslations = {
8952
9000
  },
8953
9001
  ru: {
8954
9002
  translation: {
9003
+ "packs.manualInstall": "Импорт",
9004
+ "packs.manualInstall.title": "Ручная установка пакета",
9005
+ "packs.manualInstall.instructions": "Вставьте сюда JSON-файл конфигурации пакета, который вы хотите установить.",
9006
+ "packs.manualInstall.placeholder": "{\"name\": \"My Pack\", \"description\": \"...\", \"models\": [...], \"data\": [...]}",
9007
+
8955
9008
  "dataimporter.excelPreview": "Предварительный просмотр данных Excel",
8956
9009
  "dataimporter.previewNote": "Примечание: Это предварительный просмотр первых нескольких строк. Пустые ячейки отображаются как \"(пусто)\".",
8957
9010
  "dataimporter.nullValue": "(пусто)",
@@ -8988,6 +9041,7 @@ export const websiteTranslations = {
8988
9041
  "suggested_prompt.recipes.prompt": "Коллекция кулинарных рецептов с ингредиентами, инструкциями и описанием приготовления. время.",
8989
9042
 
8990
9043
  "api.data.duplicateValue": "Значение '{{value}}' уже существует для уникального поля '{{field}}'.",
9044
+ "api.data.relationFilterFailed": "Значение {{value}} для поля {{field}} не соответствует заданному фильтру связи.",
8991
9045
  "api.model.invalidStructure": "Модель недействительна. Некоторые поля неверны.",
8992
9046
  "api.validate.fieldArray": "Поле '{{0}}' должно быть массивом.",
8993
9047
  "api.validate.invalidMimeType": "Недопустимый тип файла '{{type}}'. Допустимые типы: {{authorized}}.",
@@ -10361,6 +10415,10 @@ export const websiteTranslations = {
10361
10415
  },
10362
10416
  ar: {
10363
10417
  translation: {
10418
+ "packs.manualInstall": "استيراد",
10419
+ "packs.manualInstall.title": "تثبيت الحزمة يدويًا",
10420
+ "packs.manualInstall.instructions": "الصق ملف JSON الخاص بتكوين الحزمة التي تريد تثبيتها هنا",
10421
+ "packs.manualInstall.placeholder": "{\"name\": \"My Pack\", \"description\": \"...\", \"models\": [...], \"data\": [...]}",
10364
10422
  "dataimporter.excelPreview": "معاينة بيانات إكسل",
10365
10423
  "dataimporter.previewNote": "ملاحظة: هذه معاينة للصفوف القليلة الأولى. تُعرض الخلايا الفارغة كـ \"(فارغة)\".",
10366
10424
  "dataimporter.nullValue": "(فارغة)",
@@ -10396,6 +10454,7 @@ export const websiteTranslations = {
10396
10454
  "suggested_prompt.recipes.prompt": "مجموعة من وصفات الطبخ، مع المكونات، والتعليمات، ومدة التحضير.",
10397
10455
 
10398
10456
  "api.data.duplicateValue": "القيمة '{{value}}' موجودة بالفعل للحقل الفريد '{{field}}'.",
10457
+ "api.data.relationFilterFailed": "القيمة {{value}} للحقل {{field}} لا تتطابق مع مرشح العلاقة المحدد.",
10399
10458
  "api.model.invalidStructure": "النموذج غير صالح. بعض الحقول غير صحيحة.",
10400
10459
  "api.validate.fieldArray": "يجب أن يكون الحقل '{{0}}' مصفوفة.",
10401
10460
  "api.validate.invalidMimeType": "نوع الملف '{{type}}' غير صالح. الأنواع المسموح بها هي: {{authorized}}.",
@@ -11787,6 +11846,11 @@ export const websiteTranslations = {
11787
11846
  },
11788
11847
  sv: {
11789
11848
  translation: {
11849
+ "packs.manualInstall": "Importera",
11850
+ "packs.manualInstall.title": "Manuell paketinstallation",
11851
+ "packs.manualInstall.instructions": "Klistra in konfigurations-JSON-filen för paketet du vill installera här.",
11852
+ "packs.manualInstall.placeholder": "{\"namn\": \"Mitt paket\", \"beskrivning\": \"...\", \"modeller\": [...], \"data\": [...]}",
11853
+
11790
11854
  "dataimporter.excelPreview": "Förhandsgranskning av Excel-data",
11791
11855
  "dataimporter.previewNote": "Obs! Detta är en förhandsgranskning av de första raderna. Tomma celler visas som \"(tomma)\"",
11792
11856
  "dataimporter.nullValue": "(tomma)",
@@ -11823,6 +11887,7 @@ export const websiteTranslations = {
11823
11887
  "suggested_prompt.recipes.prompt": "En samling matlagningsrecept, med ingredienser, instruktioner, och förberedelsetid.",
11824
11888
 
11825
11889
  "api.data.duplicateValue": "Värdet '{{value}}' finns redan för det unika fältet '{{field}}'.",
11890
+ "api.data.relationFilterFailed": "Värdet {{value}} för fältet {{field}} matchar inte det definierade relationsfiltret.",
11826
11891
  "api.model.invalidStructure": "Modellen är ogiltig. Vissa fält är felaktiga.",
11827
11892
  "api.validate.fieldArray": "Fältet '{{0}}' måste vara en array.",
11828
11893
  "api.validate.invalidMimeType": "Ogiltig filtyp '{{type}}'. Tillåtna typer är: {{authorized}}.",
@@ -13186,6 +13251,11 @@ export const websiteTranslations = {
13186
13251
  },
13187
13252
  el: {
13188
13253
  translation: {
13254
+ "packs.manualInstall": "Εισαγωγή",
13255
+ "packs.manualInstall.title": "Χειροκίνητη εγκατάσταση πακέτου",
13256
+ "packs.manualInstall.instructions": "Επικολλήστε εδώ το JSON διαμόρφωσης του πακέτου που θέλετε να εγκαταστήσετε.",
13257
+ "packs.manualInstall.placeholder": "{\"name\": \"Το πακέτο μου\", \"περιγραφή\": \"...\", \"μοντέλα\": [...], \"δεδομένα\": [...]}",
13258
+
13189
13259
  "dataimporter.excelPreview": "Προεπισκόπηση δεδομένων Excel",
13190
13260
  "dataimporter.previewNote": "Σημείωση: Αυτή είναι μια προεπισκόπηση των πρώτων γραμμών. Τα κενά κελιά εμφανίζονται ως \"(κενό)\".",
13191
13261
  "dataimporter.nullValue": "(κενό)",
@@ -13222,6 +13292,8 @@ export const websiteTranslations = {
13222
13292
  "suggested_prompt.recipes.prompt": "Μια συλλογή συνταγών μαγειρικής, με υλικά, οδηγίες και χρόνο προετοιμασίας.",
13223
13293
 
13224
13294
  "api.data.duplicateValue": "Η τιμή '{{value}}' υπάρχει ήδη για το μοναδικό πεδίο '{{field}}'.",
13295
+ "api.data.relationFilterFailed": "Η τιμή {{value}} για το πεδίο {{field}} δεν ταιριάζει με το καθορισμένο φίλτρο σχέσης.",
13296
+
13225
13297
  "api.model.invalidStructure": "Το μοντέλο δεν είναι έγκυρο. Ορισμένα πεδία είναι λανθασμένα.",
13226
13298
  "api.validate.fieldArray": "Το πεδίο '{{0}}' πρέπει να είναι ένας πίνακας.",
13227
13299
  "api.validate.invalidMimeType": "Μη έγκυρος τύπος αρχείου '{{type}}'. Οι επιτρεπόμενοι τύποι είναι: {{authorized}}.",
@@ -14592,6 +14664,12 @@ export const websiteTranslations = {
14592
14664
  },
14593
14665
  fa: {
14594
14666
  "translation": {
14667
+
14668
+ "packs.manualInstall": "وارد کردن",
14669
+ "packs.manualInstall.title": "نصب دستی بسته",
14670
+ "packs.manualInstall.instructions": "فایل JSON پیکربندی بسته‌ای که می‌خواهید نصب کنید را اینجا جای‌گذاری کنید.",
14671
+ "packs.manualInstall.placeholder": "{\"name\": \"بسته من\", \"description\": \"...\", \"models\": [...], \"data\": [...]}",
14672
+
14595
14673
  "dataimporter.excelPreview": "پیش‌نمایش داده‌های اکسل",
14596
14674
  "dataimporter.previewNote": "توجه: این پیش‌نمایشی از چند ردیف اول است. سلول‌های خالی به صورت \"(empty)\" نمایش داده می‌شوند.",
14597
14675
  "dataimporter.nullValue": "(empty)",
@@ -14629,6 +14707,8 @@ export const websiteTranslations = {
14629
14707
  "suggested_prompt.recipes.prompt": "مجموعه‌ای از دستور پخت غذا، با مواد لازم، دستورالعمل و زمان آماده‌سازی.",
14630
14708
 
14631
14709
  "api.data.duplicateValue": "مقدار '{{value}}' از قبل برای فیلد منحصر به فرد '{{field}}' وجود دارد.",
14710
+ "api.data.relationFilterFailed": "مقدار {{value}} برای فیلد {{field}} با فیلتر رابطه تعریف شده مطابقت ندارد.",
14711
+
14632
14712
  "api.model.invalidStructure": "مدل نامعتبر است. برخی از فیلدها نادرست هستند.",
14633
14713
  "api.validate.fieldArray": "فیلد '{{0}}' باید یک آرایه باشد.",
14634
14714
  "api.validate.invalidMimeType": "نوع فایل '{{type}}' نامعتبر است. انواع مجاز عبارتند از: {{authorized}}.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "data-primals-engine",
3
- "version": "1.2.6-rc3",
3
+ "version": "1.3.0",
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,6 +33,11 @@
33
33
  "brace-expansion": "2.0.2",
34
34
  "prismjs": "1.30.0"
35
35
  },
36
+ "overrides": {
37
+ "refractor": {
38
+ "prismjs": "1.30.0"
39
+ }
40
+ },
36
41
  "repository": {
37
42
  "type": "git",
38
43
  "url": "https://github.com/anonympins/data-primals-engine.git"
@@ -50,6 +55,7 @@
50
55
  "react-query": ">=3.0.0"
51
56
  },
52
57
  "dependencies": {
58
+ "@langchain/anthropic": "^0.3.26",
53
59
  "@langchain/core": "^0.3.66",
54
60
  "@langchain/deepseek": "^0.1.0",
55
61
  "@langchain/google-genai": "^0.2.16",
@@ -88,6 +94,7 @@
88
94
  "request-ip": "^3.3.0",
89
95
  "sanitize-html": "^2.17.0",
90
96
  "sirv": "^3.0.1",
97
+ "stripe": "^18.4.0",
91
98
  "swagger-ui-express": "^5.0.1",
92
99
  "tar": "^7.4.3",
93
100
  "uniqid": "^5.4.0",
package/server.js CHANGED
@@ -7,6 +7,7 @@ import process from "node:process";
7
7
  import {Config, Engine, BenchmarkTool, GameObject, Logger} from "./src/index.js";
8
8
  import sirv from "sirv";
9
9
  import express from "express";
10
+ import {port} from "./src/constants.js";
10
11
 
11
12
  Config.Set("modules", ["mongodb", "data", "file", "bucket", "workflow","user", "assistant", "swagger"])
12
13
  Config.Set("middlewares", []);
@@ -27,10 +28,9 @@ if (process.argv.length === 3) {
27
28
  }
28
29
 
29
30
 
30
- const port = process.env.PORT || 7633;
31
- engine.start(port, async (r) => {
31
+ const realPort = process.env.PORT || port;
32
+ engine.start(realPort, async (r) => {
32
33
  const logger = engine.getComponent(Logger);
33
- console.log("Server started on port " + port);
34
+ console.log("Server started on port " + realPort);
34
35
  timer.stop();
35
-
36
36
  });
package/src/constants.js CHANGED
@@ -24,6 +24,8 @@ export const dbName = "engine";
24
24
  */
25
25
  export const host = 'localhost'; // or myhost.tld
26
26
 
27
+ export const port = 7633;
28
+
27
29
  /**
28
30
  * Cookie secret key (if COOKIES_SECRET is set, it will override this variable)
29
31
  * @type {string}
@@ -301,3 +303,7 @@ metaModels['erp'] = { load: [ 'accountingExercise', 'accountingLineItem', 'accou
301
303
  export const allowedFields = ['locked', 'hiddenable', 'anonymized', 'condition', 'color', 'index', 'type', 'required', 'hint', 'default', 'validate', 'unique', 'name', 'placeholder', 'asMain'];
302
304
 
303
305
 
306
+
307
+ export const getHost = () => {
308
+ return process.env.HOST || host || 'localhost';
309
+ }
@@ -420,6 +420,7 @@ export const defaultModels = {
420
420
  { "name": "products", "type": "relation", "relation": "cartItem", "required": true, multiple: true },
421
421
  { "name": "customer", "type": "relation", relation: 'user' }, // Relation vers le modèle 'user'
422
422
  { "name": "totalAmount", "type": "number", "required": true },
423
+ { "name": "paymentIntentId", "type": "string", "hint": "Stripe Payment Intent ID for this order." },
423
424
  { "name": "currency", "type": "relation", "relation": "currency" },
424
425
  { "name": "paymentMethod", "type": "string" },
425
426
  { "name": "shippingAddress", "type": "relation", "relation": "location" },
@@ -506,6 +507,7 @@ export const defaultModels = {
506
507
  name: "return",
507
508
  "description": "",
508
509
  fields: [
510
+ { "name": "order", "type": "relation", "relation": "order", "required": true },
509
511
  { "name": "user", "type": "relation", relation: "user" },
510
512
  { "name": "reason", "type": "string", maxlength: 2048, required: true },
511
513
  { "name": "channel", "type": "relation", relation: 'channel' },
@@ -897,7 +899,7 @@ export const defaultModels = {
897
899
  name: 'type',
898
900
  type: 'enum',
899
901
  required: true,
900
- items: ['UpdateData', 'CreateData', 'DeleteData', 'ExecuteScript', 'CallWebhook', 'SendEmail', 'Wait', 'GenerateAIContent'],
902
+ items: ['UpdateData', 'CreateData', 'DeleteData', 'ExecuteScript', 'HttpRequest', 'SendEmail', 'Wait', 'GenerateAIContent', 'ExecuteServiceFunction'],
901
903
  hint: "The type of operation to perform."
902
904
  },
903
905
  // For UpdateData / CreateData / DeleteData
@@ -923,14 +925,11 @@ export const defaultModels = {
923
925
  // For CreateData
924
926
  { name: 'dataToCreate', condition: {$eq: ["$type", "CreateData"]}, type: 'code', language: 'json', default: {}, hint: "Object template for the new document to create" },
925
927
 
926
- // For ExecuteScript
927
- { name: 'script', condition: {$eq: ["$type", "ExecuteScript"]}, type: 'code', hint: "The script to execute." },
928
-
929
- // For CallWebhook
930
- { name: 'url', condition: {$eq: ["$type", "CallWebhook"]}, type: 'string', hint: "The URL to call." },
931
- { name: 'method', condition: {$eq: ["$type", "CallWebhook"]}, type: 'enum', items: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'], default: 'POST', hint: "HTTP method." },
932
- { name: 'headers', condition: {$eq: ["$type", "CallWebhook"]}, type: 'code', language: 'json',default: {}, hint: "HTTP headers as key-value pairs." },
933
- { name: 'body', condition: {$eq: ["$type", "CallWebhook"]}, type: 'code', language: 'json',default: {}, hint: "Request body, can include variables." },
928
+ // For HttpRequest
929
+ { name: 'url', condition: {$eq: ["$type", "HttpRequest"]}, type: 'string', hint: "The URL to call." },
930
+ { name: 'method', condition: {$eq: ["$type", "HttpRequest"]}, type: 'enum', items: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'], default: 'POST', hint: "HTTP method." },
931
+ { name: 'headers', condition: {$eq: ["$type", "HttpRequest"]}, type: 'code', language: 'json',default: {}, hint: "HTTP headers as key-value pairs." },
932
+ { name: 'body', condition: {$eq: ["$type", "HttpRequest"]}, type: 'code', language: 'json',default: {}, hint: "Request body, can include variables." },
934
933
 
935
934
  // For SendEmail
936
935
  {
@@ -977,7 +976,15 @@ export const defaultModels = {
977
976
  condition: { $eq: ["$type", "GenerateAIContent"] },
978
977
  type: 'richtext', // richtext est bien pour les longs prompts
979
978
  hint: "Le modèle de prompt. Utilise des variables comme {triggerData.field} ou {context.variable}."
980
- }
979
+ },
980
+
981
+ // For ExecuteScript
982
+ { name: 'script', condition: {$eq: ["$type", "ExecuteScript"]}, type: 'code', language: 'javascript', hint: "The script to execute." },
983
+
984
+ // For ExecuteServiceFunction
985
+ { name: 'serviceName', condition: {$eq: ["$type", "ExecuteServiceFunction"]}, type: 'string', hint: "The name of the registered service to call (e.g., 'stripe')." },
986
+ { name: 'functionName', condition: {$eq: ["$type", "ExecuteServiceFunction"]}, type: 'string', hint: "The name of the function to execute within the service." },
987
+ { name: 'args', condition: {$eq: ["$type", "ExecuteServiceFunction"]}, type: 'code', language: 'json', default: [], hint: "An array of arguments to pass to the function. Can include variables." }
981
988
 
982
989
  ]
983
990
  },
@@ -1447,6 +1454,12 @@ export const defaultModels = {
1447
1454
  default: "POST",
1448
1455
  hint: "The HTTP method required to call this endpoint."
1449
1456
  },
1457
+ {
1458
+ name: 'isPublic',
1459
+ type: 'boolean',
1460
+ default: false,
1461
+ hint: "Si coché, ce point d'accès sera accessible sans authentification."
1462
+ },
1450
1463
  {
1451
1464
  name: "code",
1452
1465
  type: "code",
package/src/filter.js CHANGED
@@ -175,15 +175,45 @@ export const isConditionMet = (model, cond, formData, allModels, user) => {
175
175
 
176
176
  if (!condition) return true;
177
177
 
178
+ // Cas 0: Condition est une valeur primitive (string, number, boolean)
179
+ // On la considère comme toujours vraie (comportement de searchData)
180
+ if (typeof condition !== 'object' || condition === null) {
181
+ return true;
182
+ }
183
+
178
184
  // Cas 1: Condition simple {field: value} → transformée en {field: {$eq: value}}
179
185
  if (typeof condition === 'object' && !Array.isArray(condition)) {
180
186
  const keys = Object.keys(condition);
187
+
188
+ // Cas spécial pour les conditions de type {field: value} (pas d'opérateur $)
181
189
  if (keys.length === 1 && !keys[0].startsWith('$') &&
182
190
  typeof condition[keys[0]] !== 'object') {
183
- const simpleCondition = {
184
- [keys[0]]: {$eq: condition[keys[0]]}
185
- };
186
- return evaluateSingleCondition(model, simpleCondition, formData, allModels, user);
191
+ const fieldName = keys[0];
192
+ const value = condition[fieldName];
193
+
194
+ // Si la valeur est null/undefined, on vérifie simplement l'existence
195
+ if (value === null || value === undefined) {
196
+ return formData[fieldName] === value;
197
+ }
198
+
199
+ // Sinon on fait une comparaison d'égalité simple
200
+ return formData[fieldName] == value;
201
+ }
202
+
203
+ // Cas spécial pour les tableaux - vérifie si la valeur est incluse
204
+ if (keys.length === 1 && !keys[0].startsWith('$') &&
205
+ Array.isArray(condition[keys[0]])) {
206
+ const fieldName = keys[0];
207
+ const values = condition[fieldName];
208
+ const fieldValue = formData[fieldName];
209
+
210
+ // Si le champ est aussi un tableau, vérifie l'intersection
211
+ if (Array.isArray(fieldValue)) {
212
+ return fieldValue.some(v => values.includes(v));
213
+ }
214
+
215
+ // Sinon vérifie si la valeur est dans le tableau
216
+ return values.includes(fieldValue);
187
217
  }
188
218
  }
189
219
 
@@ -218,4 +248,4 @@ export const isConditionMet = (model, cond, formData, allModels, user) => {
218
248
 
219
249
  // Cas 4: Tous les autres cas (conditions normales avec opérateurs)
220
250
  return evaluateSingleCondition(model, condition, formData, allModels, user);
221
- };
251
+ };
package/src/i18n.js CHANGED
@@ -1121,7 +1121,7 @@ export const translations = {
1121
1121
  "CreateData": "Créer une donnée",
1122
1122
  "DeleteData": "Supprimer une donnée",
1123
1123
  "ExecuteScript": "Exécuter un script",
1124
- "CallWebhook": "Appeler un Webhook",
1124
+ "HttpRequest": "Appeler un Webhook",
1125
1125
  "SendEmail": "Envoyer un Email",
1126
1126
  "Wait": "Attendre",
1127
1127
  "GenerateAIContent": "Générer un contenu IA",