data-primals-engine 1.3.0 → 1.3.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.
- package/CHANGELOG.md +440 -0
- package/Dockerfile +19 -9
- package/DockerfileTest +19 -9
- package/README.md +58 -24
- package/client/package-lock.json +172 -1
- package/client/package.json +1 -0
- package/client/src/App.scss +4 -0
- package/client/src/CalendarConfigModal.jsx +60 -21
- package/client/src/CalendarView.jsx +107 -0
- package/client/src/CalendarView.scss +9 -0
- package/client/src/DataLayout.jsx +54 -27
- package/client/src/DataTable.jsx +19 -2
- package/client/src/Dialog.scss +4 -3
- package/client/src/Field.jsx +3 -2
- package/client/src/HistoryDialog.jsx +253 -0
- package/client/src/HistoryDialog.scss +320 -0
- package/client/src/ModelCreator.jsx +36 -15
- package/client/src/ModelCreatorField.jsx +5 -6
- package/client/src/ViewSwitcher.jsx +6 -6
- package/client/src/hooks/useTutorials.jsx +1 -1
- package/client/src/translations.js +298 -1
- package/package.json +2 -2
- package/src/email.js +1 -1
- package/src/engine.js +6 -6
- package/src/events.js +15 -5
- package/src/gameObject.js +0 -29
- package/src/modules/data/data.history.js +490 -0
- package/src/modules/data/data.js +139 -108
- package/src/modules/data/data.routes.js +4 -4
- package/src/modules/mongodb.js +17 -1
- package/src/modules/user.js +9 -3
- package/src/packs.js +2 -1
- package/src/services/stripe.js +67 -13
- package/src/workers/crypto-worker.js +3 -3
- package/test/data.backup.integration.test.js +5 -0
- package/test/data.history.integration.test.js +193 -0
- package/test/data.integration.test.js +79 -39
- package/test/events.test.js +27 -27
- package/test/globalTeardown.js +1 -0
- package/test/model.integration.test.js +5 -0
- package/test/workflow.actions.integration.test.js +2 -0
- package/test/workflow.integration.test.js +2 -0
- package/test/workflow.robustness.test.js +2 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
.history-dialog {
|
|
2
|
+
|
|
3
|
+
.dialog-content {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
height: 100%;
|
|
7
|
+
padding: 0; // Le padding sera sur le content wrapper
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.history-dialog-content {
|
|
12
|
+
overflow-y: auto;
|
|
13
|
+
padding: 1rem;
|
|
14
|
+
background-color: #f4f4f4;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.history-list {
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
gap: 1rem;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.history-entry {
|
|
24
|
+
border: 1px solid #ddd;
|
|
25
|
+
border-radius: 4px;
|
|
26
|
+
background-color: #fff;
|
|
27
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
|
|
28
|
+
|
|
29
|
+
.history-entry-header {
|
|
30
|
+
display: flex;
|
|
31
|
+
align-items: center;
|
|
32
|
+
gap: 1rem;
|
|
33
|
+
padding: 0.5rem 1rem;
|
|
34
|
+
background-color: #f9f9f9;
|
|
35
|
+
border-bottom: 1px solid #eee;
|
|
36
|
+
font-size: 0.9em;
|
|
37
|
+
color: #333;
|
|
38
|
+
|
|
39
|
+
.op-badge {
|
|
40
|
+
padding: 0.2rem 0.6rem;
|
|
41
|
+
border-radius: 12px;
|
|
42
|
+
color: white;
|
|
43
|
+
font-weight: bold;
|
|
44
|
+
font-size: 0.8em;
|
|
45
|
+
text-transform: uppercase;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.op-insert { background-color: #28a745; } // green
|
|
49
|
+
.op-update { background-color: #007bff; } // blue
|
|
50
|
+
.op-delete { background-color: #dc3545; } // red
|
|
51
|
+
|
|
52
|
+
.history-version {
|
|
53
|
+
margin-left: auto;
|
|
54
|
+
font-weight: bold;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.history-entry-data {
|
|
59
|
+
padding: 1rem;
|
|
60
|
+
|
|
61
|
+
pre {
|
|
62
|
+
white-space: pre-wrap;
|
|
63
|
+
word-wrap: break-word;
|
|
64
|
+
background-color: #2d2d2d;
|
|
65
|
+
color: #f8f8f2;
|
|
66
|
+
padding: 1rem;
|
|
67
|
+
border-radius: 4px;
|
|
68
|
+
margin: 0;
|
|
69
|
+
font-family: 'Courier New', Courier, monospace;
|
|
70
|
+
font-size: 0.85em;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// In C:/Dev/data-primals-engine/client/src/HistoryDialog.scss
|
|
76
|
+
|
|
77
|
+
.history-dialog .dialog-content {
|
|
78
|
+
// The new layout handles its own padding and structure.
|
|
79
|
+
padding: 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.history-dialog-content-split {
|
|
83
|
+
display: flex;
|
|
84
|
+
gap: 1rem;
|
|
85
|
+
height: 65vh; /* Adjust height as needed */
|
|
86
|
+
max-height: 700px;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.history-list-container {
|
|
90
|
+
flex: 1;
|
|
91
|
+
overflow-y: auto;
|
|
92
|
+
border-right: 1px solid #e2e2e2;
|
|
93
|
+
padding: 0 1rem 1rem 0;
|
|
94
|
+
min-width: 300px;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.history-preview-container {
|
|
98
|
+
flex: 0.8; /* Give preview a bit more space */
|
|
99
|
+
display: flex;
|
|
100
|
+
flex-direction: column;
|
|
101
|
+
min-width: 300px;
|
|
102
|
+
|
|
103
|
+
.preview-header {
|
|
104
|
+
display: flex;
|
|
105
|
+
justify-content: space-between;
|
|
106
|
+
align-items: center;
|
|
107
|
+
margin-bottom: 0.5rem;
|
|
108
|
+
padding-bottom: 0.5rem;
|
|
109
|
+
border-bottom: 1px solid #e2e2e2;
|
|
110
|
+
|
|
111
|
+
h4 {
|
|
112
|
+
margin: 0;
|
|
113
|
+
padding: 0;
|
|
114
|
+
border: none;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.btn-revert {
|
|
118
|
+
padding: 0.3rem 0.8rem;
|
|
119
|
+
font-size: 0.85em;
|
|
120
|
+
background-color: #ffc107; // A warning color like amber
|
|
121
|
+
color: black;
|
|
122
|
+
border: 1px solid #e0a800;
|
|
123
|
+
|
|
124
|
+
&:hover:not(:disabled) {
|
|
125
|
+
background-color: #e0a800;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
&:disabled {
|
|
129
|
+
background-color: #ffca2c;
|
|
130
|
+
opacity: 0.7;
|
|
131
|
+
cursor: not-allowed;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.history-preview-area {
|
|
138
|
+
flex-grow: 1;
|
|
139
|
+
overflow-y: auto;
|
|
140
|
+
background-color: #f9f9f9;
|
|
141
|
+
padding: 0.75rem;
|
|
142
|
+
border: 1px solid #ddd;
|
|
143
|
+
border-radius: 4px;
|
|
144
|
+
|
|
145
|
+
pre {
|
|
146
|
+
white-space: pre-wrap;
|
|
147
|
+
word-wrap: break-word;
|
|
148
|
+
margin: 0;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.placeholder {
|
|
152
|
+
color: #666;
|
|
153
|
+
display: flex;
|
|
154
|
+
align-items: center;
|
|
155
|
+
justify-content: center;
|
|
156
|
+
height: 100%;
|
|
157
|
+
text-align: center;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.history-entry {
|
|
162
|
+
border: 1px solid #ddd;
|
|
163
|
+
border-radius: 4px;
|
|
164
|
+
margin-bottom: 0.75rem;
|
|
165
|
+
padding: 0.75rem;
|
|
166
|
+
transition: border-color 0.2s, box-shadow 0.2s;
|
|
167
|
+
|
|
168
|
+
&.selected {
|
|
169
|
+
border-color: #007bff;
|
|
170
|
+
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.history-entry-header {
|
|
175
|
+
display: flex;
|
|
176
|
+
align-items: center;
|
|
177
|
+
flex-wrap: wrap;
|
|
178
|
+
gap: 0.5rem 1rem;
|
|
179
|
+
margin-bottom: 0.5rem;
|
|
180
|
+
|
|
181
|
+
.btn-preview {
|
|
182
|
+
margin-left: auto;
|
|
183
|
+
padding: 0.2rem 0.6rem;
|
|
184
|
+
font-size: 0.8em;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.history-entry-data pre {
|
|
189
|
+
background-color: #f0f0f0;
|
|
190
|
+
padding: 0.5rem;
|
|
191
|
+
border-radius: 4px;
|
|
192
|
+
max-height: 150px;
|
|
193
|
+
overflow-y: auto;
|
|
194
|
+
margin: 0;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* Dark Theme Adjustments */
|
|
198
|
+
.dark .history-list-container { border-right-color: #3f3f3f;
|
|
199
|
+
}
|
|
200
|
+
.dark .history-preview-container .preview-header {
|
|
201
|
+
border-bottom-color: #3f3f3f;
|
|
202
|
+
.btn-revert {
|
|
203
|
+
background-color: #d8a100;
|
|
204
|
+
border-color: #b38600;
|
|
205
|
+
color: white;
|
|
206
|
+
&:hover:not(:disabled) {
|
|
207
|
+
background-color: #b38600;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
.dark .history-preview-area { background-color: #2a2a2a; border-color: #3f3f3f; }
|
|
212
|
+
.dark .history-entry { border-color: #3f3f3f; }
|
|
213
|
+
.dark .history-entry.selected { border-color: #58a6ff; box-shadow: 0 0 0 2px rgba(88, 166, 255, 0.25); }
|
|
214
|
+
.dark .history-entry-data pre { background-color: #2a2a2a; }
|
|
215
|
+
|
|
216
|
+
/* Styles for human-friendly history data display */
|
|
217
|
+
.history-entry-data {
|
|
218
|
+
padding: 0.75rem 1rem;
|
|
219
|
+
font-size: 0.9em;
|
|
220
|
+
background-color: #fdfdfd;
|
|
221
|
+
border-top: 1px solid #f0f0f0;
|
|
222
|
+
|
|
223
|
+
/* The new implementation doesn't use <pre> here anymore */
|
|
224
|
+
pre {
|
|
225
|
+
display: none;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.changes-list, .snapshot-list {
|
|
230
|
+
display: flex;
|
|
231
|
+
flex-direction: column;
|
|
232
|
+
gap: 0.5rem;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.change-detail, .snapshot-item {
|
|
236
|
+
display: grid;
|
|
237
|
+
grid-template-columns: minmax(100px, 150px) 1fr;
|
|
238
|
+
gap: 0.5rem;
|
|
239
|
+
align-items: start;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.change-field-name, .snapshot-key {
|
|
243
|
+
font-weight: 600;
|
|
244
|
+
color: #333;
|
|
245
|
+
white-space: nowrap;
|
|
246
|
+
overflow: hidden;
|
|
247
|
+
text-overflow: ellipsis;
|
|
248
|
+
padding-top: 4px; /* Align with value */
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.change-values {
|
|
252
|
+
display: flex;
|
|
253
|
+
align-items: center;
|
|
254
|
+
gap: 0.5rem;
|
|
255
|
+
background-color: #f0f0f0;
|
|
256
|
+
padding: 0.25rem 0.5rem;
|
|
257
|
+
border-radius: 4px;
|
|
258
|
+
overflow: hidden;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.change-arrow {
|
|
262
|
+
color: #888;
|
|
263
|
+
font-weight: bold;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.value-from {
|
|
267
|
+
text-decoration: line-through;
|
|
268
|
+
color: #d9534f; /* red-ish */
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.value-to {
|
|
272
|
+
color: #5cb85c; /* green-ish */
|
|
273
|
+
font-weight: 500;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.snapshot-value, .value-from, .value-to {
|
|
277
|
+
white-space: nowrap;
|
|
278
|
+
overflow: hidden;
|
|
279
|
+
text-overflow: ellipsis;
|
|
280
|
+
display: block; /* Ensure it takes up the block for ellipsis */
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.value-empty {
|
|
284
|
+
color: #999;
|
|
285
|
+
font-style: italic;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.value-object, .value-boolean {
|
|
289
|
+
font-family: 'Courier New', Courier, monospace;
|
|
290
|
+
background-color: #e9e9e9;
|
|
291
|
+
padding: 0.1rem 0.3rem;
|
|
292
|
+
border-radius: 3px;
|
|
293
|
+
display: inline-block;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.no-changes-text {
|
|
297
|
+
color: #777;
|
|
298
|
+
font-style: italic;
|
|
299
|
+
padding: 0.5rem 0;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/* Dark Theme Adjustments */
|
|
303
|
+
.dark .history-entry-data {
|
|
304
|
+
background-color: #2c2c2c;
|
|
305
|
+
border-top-color: #3a3a3a;
|
|
306
|
+
}
|
|
307
|
+
.dark .change-field-name, .dark .snapshot-key {
|
|
308
|
+
color: #e0e0e0;
|
|
309
|
+
}
|
|
310
|
+
.dark .change-values {
|
|
311
|
+
background-color: #3a3a3a;
|
|
312
|
+
}
|
|
313
|
+
.dark .value-from { color: #ff7b7b; }
|
|
314
|
+
.dark .value-to { color: #73d173; }
|
|
315
|
+
.dark .value-object, .dark .value-boolean {
|
|
316
|
+
background-color: #444;
|
|
317
|
+
}
|
|
318
|
+
.dark .no-changes-text, .dark .value-empty {
|
|
319
|
+
color: #888;
|
|
320
|
+
}
|
|
@@ -41,6 +41,7 @@ const ModelCreator = forwardRef(({ initialPrompt = '', onModelGenerated, autoGen
|
|
|
41
41
|
const [modelName, setModelName] = useState(initialModel?.name || ''); // Utilisation de initialModel
|
|
42
42
|
const [modelMaxRequestData, setModelMaxRequestData] = useState(initialModel?.maxRequestData || ''); // Utilisation de initialModel
|
|
43
43
|
const [modelDescription, setModelDescription] = useState(initialModel?.name ? t(`model_description_${initialModel?.name}`, initialModel?.description) : '');
|
|
44
|
+
const [modelHistory, setModelHistory] = useState(!!initialModel?.history || undefined);
|
|
44
45
|
const [fields, setFields] = useState([]);
|
|
45
46
|
const [changed, setChanged] = useState(false);
|
|
46
47
|
|
|
@@ -53,6 +54,7 @@ const ModelCreator = forwardRef(({ initialPrompt = '', onModelGenerated, autoGen
|
|
|
53
54
|
setModelMaxRequestData(initialModel.maxRequestData || defaultMaxRequestData);
|
|
54
55
|
setModelDescription(initialModel.name ? t(`model_description_${initialModel.name}`, initialModel.description) : '');
|
|
55
56
|
setFields([...(initialModel.fields || []).map(m => ({...m}))]);
|
|
57
|
+
setModelHistory(initialModel.history);
|
|
56
58
|
} else {
|
|
57
59
|
// Mode création : on réinitialise tout pour une nouvelle génération
|
|
58
60
|
setModelName('');
|
|
@@ -60,6 +62,7 @@ const ModelCreator = forwardRef(({ initialPrompt = '', onModelGenerated, autoGen
|
|
|
60
62
|
setFields([]);
|
|
61
63
|
setUseAI(true); // On active l'IA par défaut
|
|
62
64
|
setModelVisible(false);
|
|
65
|
+
setModelHistory(false);
|
|
63
66
|
}
|
|
64
67
|
}, [initialModel]);
|
|
65
68
|
|
|
@@ -130,6 +133,7 @@ const ModelCreator = forwardRef(({ initialPrompt = '', onModelGenerated, autoGen
|
|
|
130
133
|
setModelDescription('');
|
|
131
134
|
setModelMaxRequestData(defaultMaxRequestData);
|
|
132
135
|
setFields([{ name: '', type: 'string' }]);
|
|
136
|
+
setModelHistory(undefined);
|
|
133
137
|
}
|
|
134
138
|
setDatasToLoad(datas=>[...datas, modelName]);
|
|
135
139
|
|
|
@@ -208,6 +212,7 @@ const ModelCreator = forwardRef(({ initialPrompt = '', onModelGenerated, autoGen
|
|
|
208
212
|
name: modelName,
|
|
209
213
|
description: modelDescription,
|
|
210
214
|
maxRequestData: modelMaxRequestData,
|
|
215
|
+
history: modelHistory,
|
|
211
216
|
fields: (fields || []).map((field) => {
|
|
212
217
|
delete field['_isNewField'];
|
|
213
218
|
let otherFields = [];
|
|
@@ -312,8 +317,8 @@ const ModelCreator = forwardRef(({ initialPrompt = '', onModelGenerated, autoGen
|
|
|
312
317
|
}
|
|
313
318
|
|
|
314
319
|
const handleRenameField = (fi, oldVal) => {
|
|
315
|
-
const prompted = prompt(t('core.renameFields', 'Renommer le champ'), oldVal);
|
|
316
|
-
if (prompted !== oldVal) {
|
|
320
|
+
const prompted = window.prompt(t('core.renameFields', 'Renommer le champ'), oldVal);
|
|
321
|
+
if (prompted && prompted !== oldVal) {
|
|
317
322
|
mutationRename.mutateAsync({oldName: oldVal, newName: prompted}).then(e =>{
|
|
318
323
|
const newFields = [...fields];
|
|
319
324
|
const field = newFields[fi];
|
|
@@ -542,21 +547,22 @@ const ModelCreator = forwardRef(({ initialPrompt = '', onModelGenerated, autoGen
|
|
|
542
547
|
</div>
|
|
543
548
|
|
|
544
549
|
<div className="field">
|
|
545
|
-
<div className="flex
|
|
546
|
-
<
|
|
550
|
+
<div className="checkbox-label flex flex-1">
|
|
551
|
+
<CheckboxField
|
|
552
|
+
label={<Trans i18nKey={"history.title"}>Historique</Trans>}
|
|
553
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
554
|
+
checked={field.required}
|
|
555
|
+
onChange={(e) => {
|
|
556
|
+
const newFields = [...fields];
|
|
557
|
+
newFields[index].history = e ? { enabled: true } : undefined;
|
|
558
|
+
setFields(newFields);
|
|
559
|
+
}}
|
|
560
|
+
help={field.required && t('modelcreator.history.hint')}
|
|
561
|
+
/>
|
|
547
562
|
</div>
|
|
548
|
-
<TextField
|
|
549
|
-
multiline
|
|
550
|
-
help={t('modelcreator.field.description')}
|
|
551
|
-
id="modelDescription"
|
|
552
|
-
disabled={modelLocked}
|
|
553
|
-
value={modelDescription}
|
|
554
|
-
onChange={(e) => {
|
|
555
|
-
setModelDescription(e.target.value);
|
|
556
|
-
setChanged(true)
|
|
557
|
-
}}
|
|
558
|
-
/>
|
|
559
563
|
</div>
|
|
564
|
+
|
|
565
|
+
|
|
560
566
|
<h3><Trans i18nKey={"modelcreator.fields"}>Champs du modèle :</Trans></h3>
|
|
561
567
|
{fields.map((field, index) => <ModelCreatorField
|
|
562
568
|
key={initialModel?.name + '_field_' + index}
|
|
@@ -601,6 +607,21 @@ const ModelCreator = forwardRef(({ initialPrompt = '', onModelGenerated, autoGen
|
|
|
601
607
|
setChanged(true)
|
|
602
608
|
}}
|
|
603
609
|
/>
|
|
610
|
+
|
|
611
|
+
<div className="flex flex-no-wrap">
|
|
612
|
+
<div className="checkbox-label flex flex-1">
|
|
613
|
+
<CheckboxField
|
|
614
|
+
label={<Trans i18nKey={"history"}>Historique</Trans>}
|
|
615
|
+
help={t('modelcreator.field.history', '')}
|
|
616
|
+
disabled={modelLocked}
|
|
617
|
+
checked={!!modelHistory}
|
|
618
|
+
onChange={(e) => {
|
|
619
|
+
setModelHistory(e? { enabled: true }: false);
|
|
620
|
+
}}
|
|
621
|
+
/>
|
|
622
|
+
</div>
|
|
623
|
+
</div>
|
|
624
|
+
|
|
604
625
|
<h3><Trans i18nKey={"modelcreator.fields"}>Champs du modèle :</Trans></h3>
|
|
605
626
|
{fields.map((field, index) => <ModelCreatorField
|
|
606
627
|
key={initialModel?.name + '_field_' + index}
|
|
@@ -74,11 +74,10 @@ const ModelCreatorField = ({model, handleRenameField, handleRemoveField, handleU
|
|
|
74
74
|
}}
|
|
75
75
|
help={t('modelcreator.name.hint')}
|
|
76
76
|
required
|
|
77
|
+
after={!(!modelLocked && isLocalUser(me) && field.locked) && !field._isNewField && (
|
|
78
|
+
<Button type={"button"} className={"btn-form btn-last"}
|
|
79
|
+
onClick={() => handleRenameField(index, field.name)}><FaEdit/></Button>)}
|
|
77
80
|
/>
|
|
78
|
-
{!(!modelLocked && isLocalUser(me) && field.locked) && !field._isNewField && (
|
|
79
|
-
<Button type={"button"} className={"btn-form btn-last"}
|
|
80
|
-
onClick={() => handleRenameField(index, field.name)}><FaEdit/></Button>)}
|
|
81
|
-
|
|
82
81
|
</div>
|
|
83
82
|
|
|
84
83
|
</div>
|
|
@@ -449,11 +448,11 @@ const ModelCreatorField = ({model, handleRenameField, handleRemoveField, handleU
|
|
|
449
448
|
</div>
|
|
450
449
|
|
|
451
450
|
{!['file', 'relation', 'array', 'calculated'].includes(field.type) && (<div
|
|
452
|
-
className="flex flex-no-wrap
|
|
451
|
+
className="flex flex-no-wrap mg-item">
|
|
453
452
|
|
|
454
453
|
{['string_t', 'string', 'richtext', 'password', 'url', 'phone', 'email'].includes(field.type) && (<>
|
|
455
454
|
{hint('modelcreator.default.hint')}
|
|
456
|
-
<div className="flex flex-1">
|
|
455
|
+
<div className="flex flex-1 field-bg">
|
|
457
456
|
<TextField
|
|
458
457
|
className="flex-1"
|
|
459
458
|
value={field.default}
|
|
@@ -8,9 +8,9 @@ import { Tooltip } from 'react-tooltip';
|
|
|
8
8
|
|
|
9
9
|
import "./ViewSwitcher.scss"
|
|
10
10
|
const viewOptions = [
|
|
11
|
-
{ id: 'table', icon: <FaTable />, labelKey: 'views.table' },
|
|
12
|
-
{ id: 'kanban', icon: <FaColumns />, labelKey: 'views.kanban' },
|
|
13
|
-
|
|
11
|
+
{ id: 'table', icon: <FaTable />, labelKey: 'views.table', defaultLabel: 'Tableau' },
|
|
12
|
+
{ id: 'kanban', icon: <FaColumns />, labelKey: 'views.kanban', defaultLabel: 'Kanban' },
|
|
13
|
+
{ id: 'calendar', icon: <FaRegCalendarAlt />, labelKey: 'views.calendar', defaultLabel: 'Calendrier' },
|
|
14
14
|
];
|
|
15
15
|
|
|
16
16
|
const ViewSwitcher = ({ currentView, onViewChange, configuredViews, onConfigureView }) => {
|
|
@@ -27,10 +27,10 @@ const ViewSwitcher = ({ currentView, onViewChange, configuredViews, onConfigureV
|
|
|
27
27
|
<Button
|
|
28
28
|
onClick={() => onViewChange(view.id)}
|
|
29
29
|
className={`btn-view ${isActive ? 'active' : ''}`}
|
|
30
|
-
title={t(view.labelKey, view.
|
|
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.
|
|
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' && (
|
|
@@ -38,7 +38,7 @@ const ViewSwitcher = ({ currentView, onViewChange, configuredViews, onConfigureV
|
|
|
38
38
|
onClick={onConfigureView}
|
|
39
39
|
className="btn-view-settings"
|
|
40
40
|
data-tooltip-id="view-settings-tooltip"
|
|
41
|
-
data-tooltip-content={t('views.configure', { view: t(view.labelKey) })}
|
|
41
|
+
data-tooltip-content={t('views.configure', 'Configurer la vue {{view}}', { view: t(view.labelKey, view.defaultLabel) })}
|
|
42
42
|
>
|
|
43
43
|
<FaCog />
|
|
44
44
|
</Button>
|
|
@@ -195,7 +195,7 @@ export const useTutorials = () => {
|
|
|
195
195
|
|
|
196
196
|
// Écoute les événements de modification de données pour vérifier la progression en arrière-plan.
|
|
197
197
|
useEffect(() => {
|
|
198
|
-
const handleDataChange = (payload) => {
|
|
198
|
+
const handleDataChange = async (payload) => {
|
|
199
199
|
console.log(`[Tutoriels] Événement de données reçu.`, payload);
|
|
200
200
|
if (me?.activeTutorial) {
|
|
201
201
|
triggerTutorialCheck();
|