data-primals-engine 1.6.3 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -900
- package/client/index.js +3 -0
- package/client/package-lock.json +7563 -8811
- package/client/package.json +11 -1
- package/client/src/App.scss +29 -0
- package/client/src/AssistantChat.jsx +369 -362
- package/client/src/DataEditor.jsx +383 -383
- package/client/src/DataLayout.jsx +54 -20
- package/client/src/ModelList.jsx +280 -280
- package/client/src/ViewSwitcher.scss +0 -31
- package/client/src/_variables.scss +3 -0
- package/client/src/constants.js +81 -100
- package/client/src/contexts/CommandContext.jsx +274 -259
- package/client/vite.config.js +30 -30
- package/doc/AI-assistance.md +93 -0
- package/doc/Advanced-workflows.md +90 -0
- package/doc/Event-system.md +79 -0
- package/doc/Packs-gallery.md +73 -0
- package/doc/automation-workflows.md +102 -0
- package/doc/core-concepts.md +33 -0
- package/doc/custom-api-endpoints.md +40 -0
- package/doc/dashboards-kpis-charts.md +49 -0
- package/doc/data-management.md +120 -0
- package/doc/data-models.md +75 -0
- package/doc/index.md +14 -0
- package/doc/roles-permissions.md +43 -0
- package/doc/users.md +30 -0
- package/package.json +20 -10
- package/src/client.js +6 -4
- package/src/constants.js +1 -1
- package/src/core.js +31 -12
- package/src/defaultModels.js +1 -1
- package/src/engine.js +342 -335
- package/src/filter.js +72 -173
- package/src/migrate.js +1 -1
- package/src/modules/assistant/assistant.js +30 -20
- package/src/modules/data/data.backup.js +4 -4
- package/src/modules/data/data.js +8 -7
- package/src/modules/data/data.operations.js +186 -133
- package/src/modules/data/data.relations.js +3 -2
- package/src/modules/data/data.scheduling.js +1 -1
- package/src/modules/mongodb.js +17 -8
- package/src/modules/swagger.js +25 -5
- package/src/modules/user.js +108 -79
- package/src/modules/workflow.js +138 -3
- package/src/packs.js +5697 -5697
- package/src/profiles.js +19 -0
- package/swagger-en.yml +3391 -1550
- package/swagger-fr.yml +3385 -2896
- package/test/assistant.test.js +2 -2
- package/test/core.test.js +341 -0
- package/test/data.backup.integration.test.js +3 -1
- package/test/data.history.integration.test.js +0 -1
- package/test/data.integration.test.js +137 -2
- package/test/user.test.js +33 -29
|
@@ -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
|
|
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 {
|
|
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((
|
|
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
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
468
|
+
insertOrUpdateMutation({ record, apiCallParams });
|
|
442
469
|
} else {
|
|
443
470
|
// C'est une insertion
|
|
444
|
-
|
|
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
|
-
|
|
494
|
-
|
|
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}
|