data-primals-engine 1.4.1 → 1.4.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/README.md +23 -19
- package/client/src/AssistantChat.jsx +48 -5
- package/client/src/AssistantChat.scss +0 -1
- package/client/src/ChartConfigModal.jsx +34 -9
- package/client/src/Dashboard.jsx +396 -349
- package/client/src/DashboardChart.jsx +91 -12
- package/client/src/DataTable.jsx +817 -807
- package/client/src/Field.jsx +1784 -1782
- package/client/src/contexts/UIContext.jsx +5 -2
- package/client/src/translations.js +16818 -16750
- package/package.json +10 -2
- package/src/config.js +2 -2
- package/src/core.js +21 -3
- package/src/engine.js +2 -1
- package/src/events.js +4 -3
- package/src/middlewares/middleware-mongodb.js +3 -1
- package/src/modules/assistant/assistant.js +131 -42
- package/src/modules/bucket.js +3 -2
- package/src/modules/data/data.backup.js +374 -0
- package/src/modules/data/data.history.js +11 -8
- package/src/modules/data/data.js +190 -4551
- package/src/modules/data/data.operations.js +2790 -0
- package/src/modules/data/data.relations.js +687 -0
- package/src/modules/data/data.routes.js +132 -38
- package/src/modules/data/data.scheduling.js +274 -0
- package/src/modules/data/data.validation.js +245 -0
- package/src/modules/data/index.js +29 -1
- package/src/modules/user.js +2 -1
- package/src/modules/workflow.js +3 -2
- package/src/services/stripe.js +3 -2
|
@@ -13,7 +13,7 @@ import i18n from 'i18next';
|
|
|
13
13
|
|
|
14
14
|
import 'chartjs-adapter-date-fns';
|
|
15
15
|
import {useModelContext} from "./contexts/ModelContext.jsx";
|
|
16
|
-
import {isDate} from "../../src/core.js";
|
|
16
|
+
import {isDate, stringToHslColor} from "../../src/core.js";
|
|
17
17
|
import useWindowSize from "./hooks/useWindowSize.js";
|
|
18
18
|
import useDebounce from "./hooks/useDebounce.js";
|
|
19
19
|
|
|
@@ -73,6 +73,8 @@ const formatDateLabel = (label, locale = 'fr-FR') => {
|
|
|
73
73
|
return String(label);
|
|
74
74
|
};
|
|
75
75
|
|
|
76
|
+
|
|
77
|
+
|
|
76
78
|
const parsePotentiallyFormattedDate = (str) => {
|
|
77
79
|
if (typeof str !== 'string') return null;
|
|
78
80
|
let match = str.match(/^(\d{2})\/(\d{2})\/(\d{4})(?: (\d{2}):(\d{2}))?$/);
|
|
@@ -150,7 +152,7 @@ const processDataForChart = (inputData, config, t, timed, lang) => {
|
|
|
150
152
|
barPercentage: timed && config.type === 'bar' ? 0.9 : undefined,
|
|
151
153
|
categoryPercentage: timed && config.type === 'bar' ? 0.8 : undefined,
|
|
152
154
|
backgroundColor: (type === 'pie' || type === 'doughnut')
|
|
153
|
-
?
|
|
155
|
+
? labels.map(label => stringToHslColor(String(label))) // Génération dynamique
|
|
154
156
|
: config.chartBackgroundColor,
|
|
155
157
|
borderColor: (type === 'pie' || type === 'doughnut')
|
|
156
158
|
? '#fff'
|
|
@@ -195,6 +197,14 @@ const DashboardChart = ({ config }) => { // config peut maintenant contenir conf
|
|
|
195
197
|
const fieldDefinition = modelDefinition?.fields.find(f => f.name === config.xAxis);
|
|
196
198
|
const timed = fieldDefinition && ['datetime', 'date'].includes(fieldDefinition.type);
|
|
197
199
|
|
|
200
|
+
// État local pour gérer l'échelle de temps de manière interactive
|
|
201
|
+
const [localTimeUnit, setLocalTimeUnit] = useState(config.timeUnit || 'day');
|
|
202
|
+
|
|
203
|
+
// S'assurer que l'état local est synchronisé si la config du graphique change
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
setLocalTimeUnit(config.timeUnit || 'day');
|
|
206
|
+
}, [config.timeUnit]);
|
|
207
|
+
|
|
198
208
|
const isGroupingChart = config && ['pie', 'doughnut'].includes(config.type);
|
|
199
209
|
const requiresYAxisForValidation = config && config.aggregationType && config.aggregationType !== 'count';
|
|
200
210
|
const isValidConfig = config && config.model && config.type && config.title && config.aggregationType &&
|
|
@@ -262,25 +272,45 @@ const DashboardChart = ({ config }) => { // config peut maintenant contenir conf
|
|
|
262
272
|
},
|
|
263
273
|
};
|
|
264
274
|
|
|
275
|
+
const timeUnit = localTimeUnit; // On utilise l'état local interactif
|
|
276
|
+
|
|
277
|
+
const getTooltipFormat = (unit) => {
|
|
278
|
+
switch(unit) {
|
|
279
|
+
case 'year': return 'yyyy';
|
|
280
|
+
case 'month': return 'MMM yyyy';
|
|
281
|
+
case 'hour': return "dd MMM, HH'h'";
|
|
282
|
+
case 'minute': return 'dd MMM, HH:mm';
|
|
283
|
+
case 'day':
|
|
284
|
+
case 'week':
|
|
285
|
+
default: return 'dd MMM yyyy';
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
265
289
|
const axisOptions = {
|
|
266
290
|
...baseOptions,
|
|
267
291
|
scales: {
|
|
268
292
|
x: {
|
|
269
293
|
type: timed ? 'time' : 'category',
|
|
270
294
|
time: timed ? {
|
|
271
|
-
tooltipFormat:
|
|
272
|
-
unit:
|
|
295
|
+
tooltipFormat: getTooltipFormat(timeUnit),
|
|
296
|
+
unit: timeUnit,
|
|
273
297
|
} : {},
|
|
274
298
|
ticks: timed ? {
|
|
275
299
|
autoSkip: true,
|
|
276
300
|
maxTicksLimit: 20,
|
|
277
301
|
callback: function (value) {
|
|
278
|
-
const d = new Date();
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
302
|
+
const d = new Date(value);
|
|
303
|
+
let options;
|
|
304
|
+
switch(timeUnit) {
|
|
305
|
+
case 'year': options = { year: 'numeric' }; break;
|
|
306
|
+
case 'month': options = { month: 'short', year: 'numeric' }; break;
|
|
307
|
+
case 'day':
|
|
308
|
+
case 'week': options = { day: 'numeric', month: 'short' }; break;
|
|
309
|
+
case 'hour':
|
|
310
|
+
case 'minute':
|
|
311
|
+
default: options = { hour: '2-digit', minute: '2-digit' }; break;
|
|
312
|
+
}
|
|
313
|
+
return new Intl.DateTimeFormat(lang, options).format(d);
|
|
284
314
|
}
|
|
285
315
|
} : {}
|
|
286
316
|
},
|
|
@@ -289,7 +319,32 @@ const DashboardChart = ({ config }) => { // config peut maintenant contenir conf
|
|
|
289
319
|
}
|
|
290
320
|
},
|
|
291
321
|
};
|
|
292
|
-
|
|
322
|
+
|
|
323
|
+
const noAxisOptions = {
|
|
324
|
+
...baseOptions,
|
|
325
|
+
scales: undefined,
|
|
326
|
+
plugins: {
|
|
327
|
+
...baseOptions.plugins, // Conserve la légende et le titre de base
|
|
328
|
+
tooltip: {
|
|
329
|
+
callbacks: {
|
|
330
|
+
// Le titre de l'infobulle doit être le libellé de la section survolée (ex: "Catégorie A")
|
|
331
|
+
title: (tooltipItems) => tooltipItems[0]?.label || '',
|
|
332
|
+
|
|
333
|
+
// Le corps de l'infobulle doit afficher la valeur et son contexte (ex: "Count: 123")
|
|
334
|
+
// et non le titre général du graphique.
|
|
335
|
+
label: (tooltipItem) => {
|
|
336
|
+
const aggregationLabel = t('aggregation.' + config.aggregationType, config.aggregationType);
|
|
337
|
+
const value = tooltipItem.formattedValue || tooltipItem.raw;
|
|
338
|
+
if (config.yAxis && config.aggregationType !== 'count') {
|
|
339
|
+
const yAxisLabel = t(`field_${config.model}_${config.yAxis}`, config.yAxis);
|
|
340
|
+
return `${aggregationLabel} (${yAxisLabel}): ${value}`;
|
|
341
|
+
}
|
|
342
|
+
return `${aggregationLabel}: ${value}`;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
};
|
|
293
348
|
|
|
294
349
|
const data = !isResizing ? chartData : { labels: [], datasets: [] };
|
|
295
350
|
try {
|
|
@@ -311,7 +366,7 @@ const DashboardChart = ({ config }) => { // config peut maintenant contenir conf
|
|
|
311
366
|
</div>
|
|
312
367
|
);
|
|
313
368
|
}
|
|
314
|
-
}, [config, t, chartData, isResizing]);
|
|
369
|
+
}, [config, t, chartData, isResizing, lang, localTimeUnit]);
|
|
315
370
|
|
|
316
371
|
if (!isValidConfig) {
|
|
317
372
|
return (
|
|
@@ -353,6 +408,30 @@ const DashboardChart = ({ config }) => { // config peut maintenant contenir conf
|
|
|
353
408
|
|
|
354
409
|
return (
|
|
355
410
|
<div className="dashboard-chart-container" style={containerStyle}>
|
|
411
|
+
{/* Ajout du sélecteur d'échelle de temps directement sur le graphique */}
|
|
412
|
+
{timed && (
|
|
413
|
+
<div style={{ position: 'absolute', top: '5px', right: '5px', zIndex: 10 }}>
|
|
414
|
+
<select
|
|
415
|
+
value={localTimeUnit}
|
|
416
|
+
onChange={(e) => setLocalTimeUnit(e.target.value)}
|
|
417
|
+
style={{
|
|
418
|
+
padding: '2px 4px',
|
|
419
|
+
borderRadius: '4px',
|
|
420
|
+
border: '1px solid #ccc',
|
|
421
|
+
backgroundColor: 'white',
|
|
422
|
+
fontSize: '0.8em'
|
|
423
|
+
}}
|
|
424
|
+
title={t('charts.timeUnit', 'Échelle de temps')}
|
|
425
|
+
>
|
|
426
|
+
<option value="minute">{t('time.minute', 'Minute')}</option>
|
|
427
|
+
<option value="hour">{t('time.hour', 'Heure')}</option>
|
|
428
|
+
<option value="day">{t('time.day', 'Jour')}</option>
|
|
429
|
+
<option value="week">{t('time.week', 'Semaine')}</option>
|
|
430
|
+
<option value="month">{t('time.month', 'Mois')}</option>
|
|
431
|
+
<option value="year">{t('time.year', 'Année')}</option>
|
|
432
|
+
</select>
|
|
433
|
+
</div>
|
|
434
|
+
)}
|
|
356
435
|
{renderChart}
|
|
357
436
|
</div>
|
|
358
437
|
);
|