data-primals-engine 1.6.5 → 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 +160 -113
- package/client/index.js +3 -0
- package/client/package-lock.json +7562 -8824
- package/client/package.json +6 -2
- 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/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/package.json +15 -7
- package/src/constants.js +1 -1
- package/src/defaultModels.js +1 -1
- package/src/engine.js +342 -335
- 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 +6 -6
- package/src/modules/data/data.operations.js +79 -64
- package/src/modules/data/data.relations.js +2 -1
- package/src/modules/data/data.scheduling.js +1 -1
- package/src/modules/mongodb.js +16 -8
- package/src/modules/user.js +0 -1
- package/src/packs.js +5697 -5697
- package/src/profiles.js +19 -0
- package/swagger-en.yml +3390 -3385
- package/swagger-fr.yml +3385 -3380
- package/test/assistant.test.js +2 -2
- package/test/data.backup.integration.test.js +3 -1
- package/test/data.history.integration.test.js +0 -1
|
@@ -1,260 +1,275 @@
|
|
|
1
|
-
import React, {createContext, useContext, useState, useCallback, useRef, useEffect, useMemo} from 'react';
|
|
2
|
-
import { useQueryClient } from 'react-query';
|
|
3
|
-
import { useNotificationContext } from '../NotificationProvider.jsx';
|
|
4
|
-
import { useTranslation } from 'react-i18next';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
this.
|
|
17
|
-
|
|
18
|
-
this.
|
|
19
|
-
this.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
this.
|
|
26
|
-
this.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
// Supprimer l'historique "redo" si on exécute une nouvelle commande
|
|
34
|
-
this.history = this.history.slice(0, this.currentIndex + 1);
|
|
35
|
-
|
|
36
|
-
this.
|
|
37
|
-
this.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (!this.insertedItem?._id) throw new Error(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
this.
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
// On
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
//
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
//
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
1
|
+
import React, {createContext, useContext, useState, useCallback, useRef, useEffect, useMemo} from 'react';
|
|
2
|
+
import { useQueryClient } from 'react-query';
|
|
3
|
+
import { useNotificationContext } from '../NotificationProvider.jsx';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
|
|
6
|
+
const CommandContext = createContext({});
|
|
7
|
+
|
|
8
|
+
export const useCommand = () => useContext(CommandContext);
|
|
9
|
+
|
|
10
|
+
// --- Command Manager ---
|
|
11
|
+
class CommandManager {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.history = [];
|
|
14
|
+
this.currentIndex = -1;
|
|
15
|
+
// Les dépendances seront injectées via updateDependencies
|
|
16
|
+
this.addNotification = () => {};
|
|
17
|
+
this.t = (key) => key;
|
|
18
|
+
this.onResetQueryClient = () => {};
|
|
19
|
+
this.queryClient = null; // Ajout pour stocker le queryClient
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
updateDependencies(addNotification, t, onResetQueryClient, queryClient) {
|
|
23
|
+
this.addNotification = addNotification;
|
|
24
|
+
this.t = t;
|
|
25
|
+
this.onResetQueryClient = onResetQueryClient;
|
|
26
|
+
this.queryClient = queryClient; // Injection du client
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// La méthode execute est maintenant beaucoup plus simple.
|
|
30
|
+
// Elle n'appelle plus de fonction API, elle enregistre juste la commande.
|
|
31
|
+
add(command) {
|
|
32
|
+
try {
|
|
33
|
+
// Supprimer l'historique "redo" si on exécute une nouvelle commande
|
|
34
|
+
this.history = this.history.slice(0, this.currentIndex + 1);
|
|
35
|
+
this.history.push(command);
|
|
36
|
+
this.currentIndex++;
|
|
37
|
+
this.invalidateQueries(command.modelName, command.constructor.name);
|
|
38
|
+
} catch (e) {
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async undo() {
|
|
44
|
+
if (this.canUndo()) {
|
|
45
|
+
try {
|
|
46
|
+
const command = this.history[this.currentIndex];
|
|
47
|
+
await command.undo();
|
|
48
|
+
this.currentIndex--;
|
|
49
|
+
await this.invalidateQueries(command.modelName, command.constructor.name + 'Undo');
|
|
50
|
+
this.addNotification({ title: this.t('command.success.undo', 'Action annulée'), status: 'completed' });
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error("Command undo failed:", error);
|
|
53
|
+
this.addNotification({ title: this.t('command.error.undo', 'Erreur d\'annulation'), message: error.message, status: 'error' });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async redo() {
|
|
59
|
+
if (this.canRedo()) {
|
|
60
|
+
try {
|
|
61
|
+
this.currentIndex++;
|
|
62
|
+
const commandToRedo = this.history[this.currentIndex]; // Renommé pour plus de clarté
|
|
63
|
+
// Pour refaire, on exécute à nouveau l'action originale.
|
|
64
|
+
await commandToRedo.execute(this.context.apiCall); // *** CORRECTION: On passe la fonction apiCall ***
|
|
65
|
+
await this.invalidateQueries(commandToRedo.modelName, commandToRedo.constructor.name);
|
|
66
|
+
this.addNotification({ title: this.t('command.success.redo', 'Action rétablie'), status: 'completed' });
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error("Command redo failed:", error);
|
|
69
|
+
this.addNotification({ title: this.t('command.error.redo', 'Erreur de rétablissement'), message: error.message, status: 'error' });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
canUndo() {
|
|
75
|
+
return this.currentIndex >= 0;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
canRedo() {
|
|
79
|
+
return this.currentIndex < this.history.length - 1;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async invalidateQueries(modelName, commandType) {
|
|
83
|
+
// Solution plus fine : on invalide seulement les requêtes concernées.
|
|
84
|
+
// React Query s'occupera de rafraîchir les données de manière optimisée.
|
|
85
|
+
if (this.queryClient && modelName) {
|
|
86
|
+
// Invalide toutes les requêtes liées à ce modèle (listes, paginations, etc.)
|
|
87
|
+
await this.queryClient.invalidateQueries(['api/data', modelName]);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// --- Command Definitions ---
|
|
93
|
+
|
|
94
|
+
export class InsertCommand {
|
|
95
|
+
constructor(modelName, apiCallParams) {
|
|
96
|
+
this.modelName = modelName;
|
|
97
|
+
this.apiCallParams = apiCallParams; // Stocke directement les paramètres
|
|
98
|
+
this.insertedItem = null; // On va stocker l'objet complet inséré
|
|
99
|
+
this.successMessage = "Donnée ajoutée";
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// La méthode execute est maintenant utilisée uniquement pour le 'redo'.
|
|
103
|
+
// Elle doit refaire l'appel API initial.
|
|
104
|
+
async execute(apiCall) {
|
|
105
|
+
const redoVariables = {
|
|
106
|
+
apiCallParams: this.apiCallParams
|
|
107
|
+
};
|
|
108
|
+
const response = await apiCall(redoVariables);
|
|
109
|
+
if (!response.success) throw new Error(response.error || 'Redo (Insert) failed');
|
|
110
|
+
// On stocke l'objet complet retourné par l'API, qui inclut le nouvel _id.
|
|
111
|
+
// C'est important pour que le 'undo' suivant fonctionne.
|
|
112
|
+
this.insertedItem = response.data;
|
|
113
|
+
if (!this.insertedItem?._id) throw new Error('No inserted data returned from API');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async undo() {
|
|
117
|
+
// On garde une copie de l'item avant de le supprimer pour le redo.
|
|
118
|
+
if (!this.insertedItem?._id) throw new Error("Cannot undo insert: item ID is missing.");
|
|
119
|
+
const response = await fetch(`/api/data/${this.insertedItem._id}`, { method: 'DELETE' });
|
|
120
|
+
const result = await response.json();
|
|
121
|
+
if (!response.ok || !result.success) {
|
|
122
|
+
throw new Error(result.error || 'Undo (delete) failed');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export class UpdateCommand {
|
|
128
|
+
constructor(modelName, originalData, apiCallParams, apiCall) {
|
|
129
|
+
this.modelName = modelName;
|
|
130
|
+
this.originalData = Array.isArray(originalData) ? [...originalData] : [{ ...originalData }]; // Copie pour l'undo, s'assure que c'est un tableau
|
|
131
|
+
this.apiCallParams = apiCallParams;
|
|
132
|
+
this.apiCall = apiCall; // Stocke la fonction pour l'undo
|
|
133
|
+
this.recordIds = this.originalData.map(d => d._id);
|
|
134
|
+
this.successMessage = "Donnée mise à jour";
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// La méthode execute est utilisée pour le 'redo'.
|
|
138
|
+
// Elle refait la mise à jour avec les nouvelles données.
|
|
139
|
+
async execute(apiCall) { // *** CORRECTION: Accepte apiCall en paramètre ***
|
|
140
|
+
const redoVariables = {
|
|
141
|
+
record: this.apiCallParams.record, // Le `record` est dans les params
|
|
142
|
+
apiCallParams: this.apiCallParams
|
|
143
|
+
};
|
|
144
|
+
const response = await (apiCall || this.apiCall)(redoVariables); // Utilise l'apiCall passée ou celle stockée
|
|
145
|
+
if (!response.success) throw new Error(response.error || 'Update failed');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async undo() {
|
|
149
|
+
// Pour annuler, on ré-applique les données originales pour chaque document
|
|
150
|
+
const undoPromises = this.originalData.map(doc => {
|
|
151
|
+
const undoVariables = {
|
|
152
|
+
record: doc, // Le document à restaurer
|
|
153
|
+
apiCallParams: { ...this.apiCallParams, formData: doc } // Les paramètres, en s'assurant que formData est bien le document original
|
|
154
|
+
};
|
|
155
|
+
return this.apiCall(undoVariables);
|
|
156
|
+
});
|
|
157
|
+
await Promise.all(undoPromises);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export class DeleteCommand {
|
|
162
|
+
constructor(apiCall, modelName, itemsToDelete) {
|
|
163
|
+
this.apiCall = apiCall; // *** CORRECTION: Stocker apiCall pour l'undo ***
|
|
164
|
+
this.modelName = modelName;
|
|
165
|
+
this.itemsToDelete = Array.isArray(itemsToDelete) ? [...itemsToDelete] : [itemsToDelete];
|
|
166
|
+
this.successMessage = "Donnée(s) supprimée(s)";
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// La méthode execute est utilisée pour le 'redo'.
|
|
170
|
+
// Elle doit refaire la suppression.
|
|
171
|
+
async execute(apiCall) { // *** CORRECTION: Accepte apiCall en paramètre ***
|
|
172
|
+
// On utilise la fonction stockée dans le constructeur.
|
|
173
|
+
const response = await this.apiCall(this.itemsToDelete);
|
|
174
|
+
if (!response.success) throw new Error(response.error || 'Delete failed');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async undo() {
|
|
178
|
+
// Pour annuler, on ré-insère chaque document supprimé
|
|
179
|
+
// Note : cela crée de nouveaux _id, mais restaure les données.
|
|
180
|
+
const reinsertPromises = this.itemsToDelete.map(item => {
|
|
181
|
+
const reinsertData = { ...item };
|
|
182
|
+
delete reinsertData._id; // L'API doit générer un nouvel ID
|
|
183
|
+
delete reinsertData._hash;
|
|
184
|
+
|
|
185
|
+
return fetch('/api/data?_user=system', { // On ajoute _user=system pour tracer l'origine de l'action
|
|
186
|
+
method: 'POST',
|
|
187
|
+
headers: { 'Content-Type': 'application/json' },
|
|
188
|
+
body: JSON.stringify({ model: this.modelName, data: reinsertData }),
|
|
189
|
+
}).then(res => res.json().then(data => {
|
|
190
|
+
if (!res.ok || !data.success) {
|
|
191
|
+
throw new Error(data.error || `Undo (re-insert) failed for item ${item._id}.`);
|
|
192
|
+
}
|
|
193
|
+
// On retourne l'objet complet retourné par l'API, qui contient le nouvel _id
|
|
194
|
+
return data.data;
|
|
195
|
+
}));
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// On attend que toutes les réinsertions soient terminées.
|
|
199
|
+
const reinsertedItems = await Promise.all(reinsertPromises);
|
|
200
|
+
|
|
201
|
+
// *** CORRECTION CRUCIALE ***
|
|
202
|
+
// On met à jour la liste des items de la commande avec les nouvelles données (et les nouveaux _id).
|
|
203
|
+
// Ainsi, si un "redo" est exécuté, il ciblera les bons documents à supprimer.
|
|
204
|
+
this.itemsToDelete = reinsertedItems;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// --- Singleton Command Manager ---
|
|
209
|
+
// On crée l'instance du manager EN DEHORS du composant React.
|
|
210
|
+
// Ainsi, elle ne sera pas détruite et recréée à chaque re-rendu de l'application,
|
|
211
|
+
// ce qui permet de conserver l'historique des commandes (undo/redo).
|
|
212
|
+
const commandManagerInstance = new CommandManager();
|
|
213
|
+
|
|
214
|
+
export const createInsertCommand = (modelName, apiCallParams) => new InsertCommand(modelName, apiCallParams);
|
|
215
|
+
export const createUpdateCommand = (modelName, record, apiCallParams, apiCall) => new UpdateCommand(modelName, record, apiCallParams, apiCall);
|
|
216
|
+
export const createDeleteCommand = (apiCall, modelName, itemsToDelete) => new DeleteCommand(apiCall, modelName, itemsToDelete);
|
|
217
|
+
|
|
218
|
+
// --- Provider Component ---
|
|
219
|
+
export const CommandProvider = ({ children, onResetQueryClient }) => {
|
|
220
|
+
const { addNotification } = useNotificationContext();
|
|
221
|
+
const { t, i18n } = useTranslation();
|
|
222
|
+
const queryClient = useQueryClient(); // On récupère le client ici
|
|
223
|
+
const lang = (i18n.resolvedLanguage || i18n.language).split(/[-_]/)?.[0];
|
|
224
|
+
|
|
225
|
+
// On utilise l'instance unique et on met à jour ses dépendances via useEffect
|
|
226
|
+
// pour s'assurer qu'elle a toujours les dernières fonctions (qui sont recréées à chaque rendu).
|
|
227
|
+
const commandManagerRef = useRef(commandManagerInstance);
|
|
228
|
+
useEffect(() => {
|
|
229
|
+
commandManagerInstance.updateDependencies(addNotification, t, onResetQueryClient, queryClient);
|
|
230
|
+
}, [addNotification, t, onResetQueryClient, queryClient]);
|
|
231
|
+
|
|
232
|
+
const [canUndo, setCanUndo] = useState(commandManagerRef.current.canUndo());
|
|
233
|
+
const [canRedo, setCanRedo] = useState(commandManagerRef.current.canRedo());
|
|
234
|
+
|
|
235
|
+
const updateUndoRedoState = () => {
|
|
236
|
+
setCanUndo(commandManagerRef.current.canUndo());
|
|
237
|
+
setCanRedo(commandManagerRef.current.canRedo());
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
// Renommée en 'addCommand' pour plus de clarté.
|
|
241
|
+
const addCommand = (command) => {
|
|
242
|
+
commandManagerRef.current.add(command);
|
|
243
|
+
updateUndoRedoState();
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const undo = async () => {
|
|
247
|
+
await commandManagerRef.current.undo();
|
|
248
|
+
updateUndoRedoState();
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const redo = async () => {
|
|
252
|
+
await commandManagerRef.current.redo();
|
|
253
|
+
updateUndoRedoState();
|
|
254
|
+
};
|
|
255
|
+
// --- CORRECTION ---
|
|
256
|
+
// On inclut `setManagerContext` directement dans la valeur du contexte
|
|
257
|
+
// et on mémorise l'objet avec `useMemo` pour la stabilité.
|
|
258
|
+
const value = useMemo(() => ({
|
|
259
|
+
addCommand, // (command)
|
|
260
|
+
undo,
|
|
261
|
+
redo,
|
|
262
|
+
canUndo, // boolean
|
|
263
|
+
canRedo, // boolean
|
|
264
|
+
createInsertCommand,
|
|
265
|
+
createUpdateCommand,
|
|
266
|
+
createDeleteCommand,
|
|
267
|
+
setManagerContext: (context) => {
|
|
268
|
+
commandManagerRef.current.context = context;
|
|
269
|
+
}
|
|
270
|
+
}), [canUndo, canRedo]); // Dépendances pour la mémorisation
|
|
271
|
+
|
|
272
|
+
return (
|
|
273
|
+
<CommandContext.Provider value={value}>{children}</CommandContext.Provider>
|
|
274
|
+
);
|
|
260
275
|
};
|
package/client/vite.config.js
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import { defineConfig } from 'vite'
|
|
2
|
-
import react from '@vitejs/plugin-react'
|
|
3
|
-
|
|
4
|
-
// https://vite.dev/config/
|
|
5
|
-
export default defineConfig({
|
|
6
|
-
plugins: [react()],
|
|
7
|
-
base: "/",
|
|
8
|
-
build: {
|
|
9
|
-
outDir: "dist"
|
|
10
|
-
},
|
|
11
|
-
server : {
|
|
12
|
-
proxy: {
|
|
13
|
-
'/api': {
|
|
14
|
-
target: 'http://localhost:7633',
|
|
15
|
-
secure: false,
|
|
16
|
-
changeOrigin: true,
|
|
17
|
-
// Optionnel mais utile pour le débogage :
|
|
18
|
-
// Affiche les requêtes proxy dans la console de Vite.
|
|
19
|
-
configure: (proxy, options) => {
|
|
20
|
-
proxy.on('proxyReq', (proxyReq, req, res) => {
|
|
21
|
-
console.log(`[Vite Proxy] Forwarding request: ${req.method} ${req.url} -> ${options.target}${proxyReq.path}`);
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
fs: {
|
|
28
|
-
allow: ["./src"]
|
|
29
|
-
}
|
|
30
|
-
})
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import react from '@vitejs/plugin-react'
|
|
3
|
+
|
|
4
|
+
// https://vite.dev/config/
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
plugins: [react()],
|
|
7
|
+
base: "/",
|
|
8
|
+
build: {
|
|
9
|
+
outDir: "dist"
|
|
10
|
+
},
|
|
11
|
+
server : {
|
|
12
|
+
proxy: {
|
|
13
|
+
'/api': {
|
|
14
|
+
target: 'http://localhost:7633',
|
|
15
|
+
secure: false,
|
|
16
|
+
changeOrigin: true,
|
|
17
|
+
// Optionnel mais utile pour le débogage :
|
|
18
|
+
// Affiche les requêtes proxy dans la console de Vite.
|
|
19
|
+
configure: (proxy, options) => {
|
|
20
|
+
proxy.on('proxyReq', (proxyReq, req, res) => {
|
|
21
|
+
console.log(`[Vite Proxy] Forwarding request: ${req.method} ${req.url} -> ${options.target}${proxyReq.path}`);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
fs: {
|
|
28
|
+
allow: ["./src", "../src"]
|
|
29
|
+
}
|
|
30
|
+
})
|