data-primals-engine 1.6.5 → 1.7.1

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 +160 -113
  2. package/client/index.js +3 -0
  3. package/client/package-lock.json +8121 -8824
  4. package/client/package.json +10 -3
  5. package/client/src/AssistantChat.jsx +369 -362
  6. package/client/src/DataEditor.jsx +383 -383
  7. package/client/src/DataLayout.jsx +54 -20
  8. package/client/src/ModelList.jsx +280 -280
  9. package/client/src/ViewSwitcher.scss +0 -31
  10. package/client/src/constants.js +81 -100
  11. package/client/src/contexts/CommandContext.jsx +274 -259
  12. package/client/vite.config.js +30 -30
  13. package/doc/AI-assistance.md +93 -0
  14. package/doc/Advanced-workflows.md +90 -0
  15. package/doc/Event-system.md +79 -0
  16. package/doc/Packs-gallery.md +73 -0
  17. package/package.json +30 -16
  18. package/src/constants.js +1 -1
  19. package/src/core.js +487 -477
  20. package/src/defaultModels.js +1 -1
  21. package/src/email.js +0 -2
  22. package/src/engine.js +342 -335
  23. package/src/filter.js +348 -343
  24. package/src/migrate.js +1 -1
  25. package/src/modules/assistant/assistant.js +30 -20
  26. package/src/modules/data/data.backup.js +4 -4
  27. package/src/modules/data/data.js +311 -302
  28. package/src/modules/data/data.operations.js +79 -64
  29. package/src/modules/data/data.relations.js +2 -1
  30. package/src/modules/data/data.scheduling.js +1 -1
  31. package/src/modules/mongodb.js +16 -8
  32. package/src/modules/user.js +0 -1
  33. package/src/modules/workflow.js +1828 -1815
  34. package/src/packs.js +5701 -5697
  35. package/src/profiles.js +19 -0
  36. package/swagger-en.yml +3390 -3385
  37. package/swagger-fr.yml +3385 -3380
  38. package/test/assistant.test.js +2 -2
  39. package/test/data.backup.integration.test.js +3 -1
  40. package/test/data.history.integration.test.js +0 -1
@@ -43,7 +43,7 @@ import PackGallery from "./PackGallery.jsx";
43
43
  import TutorialsMenu from "./TutorialsMenu.jsx";
44
44
  import {FaBookAtlas} from "react-icons/fa6";
45
45
  import {AssistantChat, NotificationList} from "../index.js";
46
- import { useCommand } from './contexts/CommandContext.jsx';
46
+ import {createDeleteCommand, createInsertCommand, createUpdateCommand, useCommand} from './contexts/CommandContext.jsx';
47
47
 
48
48
  import "./DataLayout.scss"
49
49
  import WorkflowEditor from "./WorkflowEditor.jsx";
@@ -104,10 +104,10 @@ function DataLayout({refreshUI}) {
104
104
  const [isKanbanModalOpen, setKanbanModalOpen] = useState(false);
105
105
  const [isWorkflowListModalOpen, setWorkflowListModalOpen] = useState(false);
106
106
 
107
- const [showPackGallery, setShowPackGallery] = useState(false);
108
- const [checkedItems, setCheckedItems] = useState([])
107
+ const [showPackGallery, setShowPackGallery] = useState(false);
108
+ const [checkedItems, setCheckedItems] = useState([]);
109
109
 
110
- const { execute, undo, redo, canUndo, canRedo, InsertCommand, UpdateCommand, DeleteCommand, setManagerContext } = useCommand();
110
+ const { addCommand, undo, redo, canUndo, canRedo, setManagerContext } = useCommand();
111
111
  const { triggerTutorialCheck } = useTutorials();
112
112
  const { t, i18n } = useTranslation();
113
113
 
@@ -399,11 +399,13 @@ function DataLayout({refreshUI}) {
399
399
 
400
400
  const { addNotification } = useNotificationContext();
401
401
 
402
- const insertOrUpdateApiCall = useCallback(({formData, record}) => {
402
+ const insertOrUpdateApiCall = useCallback((variables) => {
403
+ const { record, apiCallParams: { formData, formRef } } = variables;
403
404
  const method = record ? 'PUT' : 'POST'; // Determine method based on record
404
405
  const url = record ? `/api/data/${record._id}` : `/api/data`; // Determine URL
405
406
 
406
407
  try {
408
+ const formElement = formRef.current;
407
409
  const fd = new FormData();
408
410
 
409
411
  let obj = {};
@@ -411,13 +413,15 @@ function DataLayout({refreshUI}) {
411
413
  if (formData[key] !== undefined)
412
414
  obj[key] = formData[key];
413
415
  }
414
- Array.from(document.querySelectorAll('.field-file input[data-field]')).forEach(input =>{
415
- const fieldName = input.dataset['field'];
416
- for (let x = 0; x < input.files.length; x++) {
417
- fd.append(`${fieldName}[${x}]`, input.files[x]);
418
- }
419
- obj[fieldName] = null;
420
- });
416
+ if (formElement) {
417
+ Array.from(formElement.querySelectorAll('.field-file input[data-field]')).forEach(input => {
418
+ const fieldName = input.dataset['field'];
419
+ for (let x = 0; x < input.files.length; x++) {
420
+ fd.append(`${fieldName}[${x}]`, input.files[x]);
421
+ }
422
+ obj[fieldName] = null;
423
+ });
424
+ }
421
425
  fd.append("_data", JSON.stringify({...obj, _hash: undefined, _id: undefined}));
422
426
  fd.append('model', selectedModel.name);
423
427
 
@@ -432,18 +436,40 @@ function DataLayout({refreshUI}) {
432
436
  }
433
437
  }, [lang, me, selectedModel]);
434
438
 
435
- const { mutate: insertOrUpdateMutation, isLoading } = useMutation(insertOrUpdateApiCall);
439
+ // La mutation react-query qui gère l'appel API et la logique post-succès.
440
+ const { mutate: insertOrUpdateMutation, isLoading } = useMutation(insertOrUpdateApiCall, {
441
+ onSuccess: (response, variables) => {
442
+ if (!response.success) {
443
+ addNotification({ title: t('command.error.execute', 'Erreur d\'exécution'), message: response.error, status: 'error' });
444
+ return;
445
+ }
436
446
 
437
- const handleFormSubmit = async (formData, record) => { // Add record parameter
447
+ const { record, apiCallParams } = variables;
448
+ let command;
449
+ if (record) { // C'était une mise à jour
450
+ command = createUpdateCommand(selectedModel.name, record, apiCallParams, insertOrUpdateApiCall);
451
+ } else { // C'était une insertion
452
+ command = createInsertCommand(selectedModel.name, { ...apiCallParams, formData: response.data });
453
+ }
454
+ addCommand(command); // On ajoute la commande à l'historique SEULEMENT si l'appel API a réussi.
455
+ addNotification({ title: command.successMessage, status: 'completed' });
456
+ queryClient.invalidateQueries(['api/data', selectedModel.name]);
457
+ },
458
+ onError: (error) => {
459
+ addNotification({ title: t('command.error.execute', 'Erreur d\'exécution'), message: error.message, status: 'error' });
460
+ }
461
+ });
462
+
463
+ const handleFormSubmit = async (formData, record, formRef) => { // Add record parameter
438
464
  let command;
465
+ const apiCallParams = { formData, record, formRef };
439
466
  if (record) {
440
467
  // C'est une mise à jour
441
- command = new UpdateCommand(insertOrUpdateApiCall, selectedModel.name, record, formData);
468
+ insertOrUpdateMutation({ record, apiCallParams });
442
469
  } else {
443
470
  // C'est une insertion
444
- command = new InsertCommand(insertOrUpdateApiCall, selectedModel.name, formData);
471
+ insertOrUpdateMutation({ record: null, apiCallParams });
445
472
  }
446
- await execute(command);
447
473
  };
448
474
 
449
475
  const updateRelationIds = (model, data) => {
@@ -490,8 +516,16 @@ function DataLayout({refreshUI}) {
490
516
  const { mutateAsync: deleteMutation } = useMutation(deleteApiCall);
491
517
 
492
518
  const handleDeletion = () => {
493
- const command = new DeleteCommand(deleteApiCall, selectedModel.name, checkedItems);
494
- execute(command);
519
+ // On appelle directement la suppression via la mutation
520
+ deleteMutation(checkedItems, {
521
+ onSuccess: () => {
522
+ // Et on crée la commande pour l'historique seulement après le succès
523
+ const command = createDeleteCommand(deleteApiCall, selectedModel.name, checkedItems, deleteApiCall);
524
+ addCommand(command);
525
+ addNotification({ title: command.successMessage, status: 'completed' });
526
+ setCheckedItems([]); // Vider la sélection
527
+ }
528
+ });
495
529
  }
496
530
  const importModelsMutation = useMutation((selectedModels) => {
497
531
  return fetch('/api/models/import', { method: 'POST', headers: {
@@ -751,7 +785,7 @@ function DataLayout({refreshUI}) {
751
785
  )}
752
786
  <div className="hidden-anchor" ref={mainPartRef}></div>
753
787
 
754
- {showDataEditor && (<DataEditor
788
+ {showDataEditor && (<DataEditor ref={mainPartRef}
755
789
  key={selectedModel?.name}
756
790
  isLoading={isLoading}
757
791
  model={selectedModel}