data-primals-engine 1.5.2 → 1.6.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.
- package/README.md +938 -915
- package/client/README.md +136 -136
- package/client/index.js +0 -1
- package/client/public/doc/API.postman_collection.json +213 -213
- package/client/public/react.svg +9 -9
- package/client/src/AddWidgetTypeModal.jsx +47 -47
- package/client/src/App.css +42 -42
- package/client/src/App.jsx +92 -150
- package/client/src/App.scss +6 -0
- package/client/src/AssistantChat.jsx +362 -363
- package/client/src/Button.jsx +12 -12
- package/client/src/Dashboard.jsx +480 -480
- package/client/src/DashboardHtmlViewItem.jsx +146 -146
- package/client/src/DashboardView.jsx +654 -654
- package/client/src/DataEditor.jsx +383 -383
- package/client/src/DataLayout.jsx +779 -808
- package/client/src/DataTable.jsx +782 -822
- package/client/src/DataTable.scss +186 -186
- package/client/src/GeolocationField.jsx +93 -93
- package/client/src/HistoryDialog.jsx +307 -307
- package/client/src/HistoryDialog.scss +319 -319
- package/client/src/HtmlViewBuilderModal.jsx +90 -90
- package/client/src/HtmlViewBuilderModal.scss +17 -17
- package/client/src/HtmlViewCard.jsx +43 -43
- package/client/src/ModelCreator.jsx +683 -686
- package/client/src/ModelCreator.scss +1 -1
- package/client/src/ModelCreatorField.jsx +950 -950
- package/client/src/ModelImporter.jsx +3 -0
- package/client/src/ModelList.jsx +280 -280
- package/client/src/Notification.jsx +136 -136
- package/client/src/PackGallery.jsx +391 -391
- package/client/src/PackGallery.scss +231 -231
- package/client/src/Pagination.jsx +143 -143
- package/client/src/RelationField.jsx +354 -354
- package/client/src/RelationSelectorWidget.jsx +172 -172
- package/client/src/RelationValue.jsx +2 -1
- package/client/src/contexts/CommandContext.jsx +260 -0
- package/client/src/contexts/ModelContext.jsx +4 -1
- package/client/src/contexts/UIContext.jsx +72 -72
- package/client/src/filter.js +263 -263
- package/client/src/index.css +90 -90
- package/package.json +6 -5
- package/perf/artillery-hooks.js +37 -37
- package/perf/setup.yml +25 -25
- package/src/constants.js +4 -0
- package/src/defaultModels.js +7 -0
- package/src/engine.js +335 -335
- package/src/events.js +232 -137
- package/src/filter.js +274 -274
- package/src/index.js +1 -0
- package/src/modules/assistant/assistant.js +225 -83
- package/src/modules/auth-google/index.js +50 -50
- package/src/modules/auth-microsoft/index.js +81 -81
- package/src/modules/data/data.core.js +118 -118
- package/src/modules/data/data.history.js +555 -555
- package/src/modules/data/data.operations.js +112 -9
- package/src/modules/data/data.relations.js +686 -686
- package/src/modules/data/data.routes.js +1879 -1879
- package/src/modules/data/data.validation.js +2 -2
- package/src/modules/file.js +247 -247
- package/src/modules/user.js +14 -9
- package/src/packs.js +2 -2
- package/src/providers.js +2 -2
- package/src/services/stripe.js +196 -196
- package/src/sso.js +193 -193
- package/test/data.history.integration.test.js +264 -264
- package/test/data.integration.test.js +1281 -1206
- package/test/events.test.js +108 -10
- package/test/model.integration.test.js +377 -377
- package/test/user.test.js +106 -1
package/client/src/App.jsx
CHANGED
|
@@ -67,6 +67,7 @@ let queryClient = new QueryClient();
|
|
|
67
67
|
export const setQueryClient = (q) => {
|
|
68
68
|
queryClient = q;
|
|
69
69
|
};
|
|
70
|
+
import {CommandProvider} from "./contexts/CommandContext.jsx";
|
|
70
71
|
|
|
71
72
|
function TopBar({header}) {
|
|
72
73
|
|
|
@@ -84,7 +85,42 @@ function TopBar({header}) {
|
|
|
84
85
|
return <>{header}</>;
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
|
|
88
|
+
// --- CORRECTION ARCHITECTURALE ---
|
|
89
|
+
// BaseLayout est maintenant une fonction de composant stable, définie en dehors de App.
|
|
90
|
+
const BaseLayout=({onResetQueryClient, refreshUI})=>{
|
|
91
|
+
const { i18n } = useTranslation();
|
|
92
|
+
const lang = (i18n.resolvedLanguage || i18n.language).split(/[-_]/)?.[0];
|
|
93
|
+
|
|
94
|
+
const changeLanguage = (newLang) => {
|
|
95
|
+
if (typeof(newLang) === 'string' && newLang) {
|
|
96
|
+
i18n.changeLanguage(newLang, (err)=>{
|
|
97
|
+
if (err) return console.error('something went wrong loading', err);
|
|
98
|
+
i18next.removeResourceBundle(lang, "dataEngineTranslations");
|
|
99
|
+
const trs = {...websiteTranslations[newLang]?.['translation']} || {};
|
|
100
|
+
i18next.addResourceBundle(newLang, 'dataEngineTranslations', trs);
|
|
101
|
+
gtag("event", "change_language "+newLang);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
changeLanguage(lang);
|
|
108
|
+
}, [lang]);
|
|
109
|
+
|
|
110
|
+
return <Layout refreshUI={refreshUI} onResetQueryClient={onResetQueryClient} header={<header className={"flex"}>
|
|
111
|
+
<Tooltip id={"header"}/>
|
|
112
|
+
<h1 className="flex-1">{seoTitle}</h1>
|
|
113
|
+
<div className="flex">
|
|
114
|
+
<FaQuestion data-tooltip-id="header" data-tooltip-content="Documentation" onClick={()=> {
|
|
115
|
+
window.open("/en/documentation/", "_blank");
|
|
116
|
+
}} />
|
|
117
|
+
</div>
|
|
118
|
+
</header>} />
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// --- CORRECTION ARCHITECTURALE ---
|
|
122
|
+
// Layout est maintenant une fonction de composant stable, définie en dehors de App.
|
|
123
|
+
function Layout({ header, routes, body, footer, refreshUI, onResetQueryClient }) {
|
|
88
124
|
const [cookies, setCookie, removeCookie] = useCookies(['username']);
|
|
89
125
|
const { i18n, t } = useTranslation();
|
|
90
126
|
const lang = (i18n.resolvedLanguage || i18n.language).split(/[-_]/)?.[0];
|
|
@@ -391,7 +427,7 @@ function Layout ({header, routes, body, footer,refreshUI}) {
|
|
|
391
427
|
|
|
392
428
|
<Route path={"/user/:user/*"} element={<>
|
|
393
429
|
{menu}
|
|
394
|
-
<UserPage notifs={[]} refreshUI={refreshUI} onAuthenticated={onAuthenticated} triggerSignin={triggerSignin}/>
|
|
430
|
+
<UserPage notifs={[]} refreshUI={refreshUI} onAuthenticated={onAuthenticated} triggerSignin={triggerSignin} onResetQueryClient={onResetQueryClient} />
|
|
395
431
|
{!me && (<></>)}
|
|
396
432
|
</>}/>
|
|
397
433
|
<Route path={"/:lang/*"} element={<>
|
|
@@ -408,98 +444,54 @@ function Layout ({header, routes, body, footer,refreshUI}) {
|
|
|
408
444
|
</div>;
|
|
409
445
|
}
|
|
410
446
|
|
|
411
|
-
function UserPage({notifs,triggerSignin,refreshUI, onAuthenticated}) {
|
|
412
|
-
const {me} = useAuthContext()
|
|
447
|
+
function UserPage({notifs,triggerSignin,refreshUI, onAuthenticated, onResetQueryClient}) {
|
|
413
448
|
const [cookies, setCookie, removeCookie] = useCookies(['username']);
|
|
414
449
|
const params = useParams();
|
|
415
450
|
const [started, setStarted] = useState(false);
|
|
416
451
|
const {t } = useTranslation()
|
|
417
452
|
|
|
418
453
|
const [prompt, setPrompt] = useLocalStorage("ai_model_prompt", null);
|
|
454
|
+
const { me } = useAuthContext();
|
|
455
|
+
const id = getUserHash(me)+'';
|
|
456
|
+
|
|
457
|
+
// NOUVEAU : On récupère l'état de la navigation
|
|
458
|
+
const location = useLocation();
|
|
459
|
+
const shouldStartTour = location.state?.startTour;
|
|
460
|
+
const isDemo = /^demo[0-9]{1,2}$/.test(me?.username);
|
|
461
|
+
const { setCurrentTourSteps, setAllTourSteps, launchedTours } = useUI();
|
|
462
|
+
|
|
463
|
+
useEffect(() => {
|
|
464
|
+
if( cookies.username){
|
|
465
|
+
onAuthenticated({username: cookies.username}, true);
|
|
466
|
+
}
|
|
467
|
+
}, [cookies.username]);
|
|
468
|
+
|
|
419
469
|
const allTourSteps = {
|
|
420
470
|
demo: [
|
|
421
|
-
{
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
position: 'top', // Position de l'info-bulle par rapport à l'élément
|
|
425
|
-
},
|
|
426
|
-
{
|
|
427
|
-
selector: '.tourStep-import-model',
|
|
428
|
-
content: t('tourpoint.importModel'),
|
|
429
|
-
position: 'top',
|
|
430
|
-
},
|
|
431
|
-
{
|
|
432
|
-
selector: '.tourStep-profile',
|
|
433
|
-
content: t('tourpoint.profile'),
|
|
434
|
-
position: 'bottom',
|
|
435
|
-
},
|
|
471
|
+
{ selector: '.tourStep-create-model', content: t('tourpoint.createModel'), position: 'top' },
|
|
472
|
+
{ selector: '.tourStep-import-model', content: t('tourpoint.importModel'), position: 'top' },
|
|
473
|
+
{ selector: '.tourStep-profile', content: t('tourpoint.profile'), position: 'bottom' },
|
|
436
474
|
],
|
|
437
475
|
guides: [
|
|
438
|
-
{
|
|
439
|
-
|
|
440
|
-
content: t('tourpoint.tutorials'),
|
|
441
|
-
position: 'bottom'
|
|
442
|
-
},
|
|
443
|
-
{
|
|
444
|
-
selector: '.tourStep-import-datapack',
|
|
445
|
-
content: t('tourpoint.importDatapack'),
|
|
446
|
-
position: 'bottom'
|
|
447
|
-
}
|
|
476
|
+
{ selector: '.tourStep-tutorials', content: t('tourpoint.tutorials'), position: 'bottom' },
|
|
477
|
+
{ selector: '.tourStep-import-datapack', content: t('tourpoint.importDatapack'), position: 'bottom' }
|
|
448
478
|
],
|
|
449
479
|
personal: [
|
|
450
|
-
{
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
placement: 'bottom',
|
|
454
|
-
},
|
|
455
|
-
{
|
|
456
|
-
selector: '.model-list [data-testid=model_contact]', // Example target, adjust as needed
|
|
457
|
-
content: t('tourpoint.model-list-contact', 'Ne perdez plus les coordonnées de vos contacts !'),
|
|
458
|
-
placement: 'bottom',
|
|
459
|
-
},
|
|
460
|
-
{
|
|
461
|
-
selector: '.model-list [data-testid=model_imageGallery]', // Example target, adjust as needed
|
|
462
|
-
content: t('tourpoint.model-list-imageGallery', 'Classez vos images, photos, illustrations...'),
|
|
463
|
-
placement: 'bottom',
|
|
464
|
-
},
|
|
480
|
+
{ selector: '.model-list [data-testid=model_budget]', content: t('tourpoint.model-list-budget', 'Gérez votre budget et votre portefeuille avec précision !'), placement: 'bottom' },
|
|
481
|
+
{ selector: '.model-list [data-testid=model_contact]', content: t('tourpoint.model-list-contact', 'Ne perdez plus les coordonnées de vos contacts !'), placement: 'bottom' },
|
|
482
|
+
{ selector: '.model-list [data-testid=model_imageGallery]', content: t('tourpoint.model-list-imageGallery', 'Classez vos images, photos, illustrations...'), placement: 'bottom' },
|
|
465
483
|
],
|
|
466
484
|
developer: [
|
|
467
|
-
{
|
|
468
|
-
selector: '.btn[data-testid=btn-documentation]',
|
|
469
|
-
content: t('tourpoint.doc', 'Prenez connaissance de la documentation, pour vous aider à utiliser l\'API ou le mode bac à sable.'),
|
|
470
|
-
placement: 'bottom',
|
|
471
|
-
},
|
|
485
|
+
{ selector: '.btn[data-testid=btn-documentation]', content: t('tourpoint.doc', 'Prenez connaissance de la documentation, pour vous aider à utiliser l\'API ou le mode bac à sable.'), placement: 'bottom' },
|
|
472
486
|
],
|
|
473
487
|
company: [
|
|
474
|
-
{
|
|
475
|
-
selector: '.btn[data-testid=btn-dashboards]',
|
|
476
|
-
content: t('tourpoint.dashboards', 'Vos dashboards vous permettent de construire et suivre votre activité en temps réel.'),
|
|
477
|
-
placement: 'bottom',
|
|
478
|
-
},
|
|
488
|
+
{ selector: '.btn[data-testid=btn-dashboards]', content: t('tourpoint.dashboards', 'Vos dashboards vous permettent de construire et suivre votre activité en temps réel.'), placement: 'bottom' },
|
|
479
489
|
],
|
|
480
490
|
engineer: [
|
|
481
|
-
|
|
482
|
-
{
|
|
483
|
-
selector: '.model-list-search-bar-container', // Example target, adjust as needed
|
|
484
|
-
content: t('tourpoint.workflows', 'Tapez "workflow" dans la barre de recherche pour obtenir les modèles d\'automatisation, ou tout autre mot-clé.'),
|
|
485
|
-
placement: 'bottom',
|
|
486
|
-
},
|
|
487
|
-
// ... more steps about workflow configuration ...
|
|
491
|
+
{ selector: '.model-list-search-bar-container', content: t('tourpoint.workflows', 'Tapez "workflow" dans la barre de recherche pour obtenir les modèles d\'automatisation, ou tout autre mot-clé.'), placement: 'bottom' },
|
|
488
492
|
],
|
|
489
493
|
};
|
|
490
|
-
const id = getUserHash(me)+'';
|
|
491
494
|
|
|
492
|
-
// NOUVEAU : On récupère l'état de la navigation
|
|
493
|
-
const location = useLocation();
|
|
494
|
-
const shouldStartTour = location.state?.startTour;
|
|
495
|
-
const isDemo = /^demo[0-9]{1,2}$/.test(me?.username);
|
|
496
|
-
const { setCurrentTourSteps, setAllTourSteps, launchedTours } = useUI();
|
|
497
|
-
|
|
498
|
-
useEffect(() => {
|
|
499
|
-
if( cookies.username){
|
|
500
|
-
onAuthenticated({username: cookies.username}, true);
|
|
501
|
-
}
|
|
502
|
-
}, [cookies.username]);
|
|
503
495
|
useEffect(() => {
|
|
504
496
|
if( isDemo && shouldStartTour){
|
|
505
497
|
setCurrentTourSteps(allTourSteps.demo);
|
|
@@ -508,90 +500,40 @@ function UserPage({notifs,triggerSignin,refreshUI, onAuthenticated}) {
|
|
|
508
500
|
}, [isDemo, shouldStartTour]);
|
|
509
501
|
|
|
510
502
|
return <>{/^demo[0-9]{1,2}$/.test(me?.username) && notifs}
|
|
511
|
-
{params.user === id && <DataLayout refreshUI={refreshUI}/>}
|
|
512
|
-
{params.user !== id &&
|
|
513
|
-
<p>Veuillez vous authentifier avec cet utilisateur "{params.user}" pour accéder à vos
|
|
514
|
-
données</p>}</>;
|
|
503
|
+
{params.user === id && <DataLayout refreshUI={refreshUI} onResetQueryClient={onResetQueryClient}/>}
|
|
504
|
+
{params.user !== id && <p>Veuillez vous authentifier avec cet utilisateur "{params.user}" pour accéder à vos données</p>}</>;
|
|
515
505
|
}
|
|
516
506
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
const
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
const [translations, setTranslations] = useState([]);
|
|
527
|
-
|
|
528
|
-
const [refreshReducer, refreshUI]= useReducer((n) => n+1, 0,() => 0);
|
|
529
|
-
|
|
530
|
-
useEffect(() => {
|
|
531
|
-
if (Array.isArray(translations)) {
|
|
532
|
-
var trs= {};
|
|
533
|
-
translations.forEach(tr =>{
|
|
534
|
-
trs[tr.key] = tr.value;
|
|
535
|
-
});
|
|
536
|
-
trs= { ...allTranslations[lang]['translation'], ...trs};
|
|
537
|
-
|
|
538
|
-
if( lastLang && lang !== lastLang) {
|
|
539
|
-
i18n.removeResourceBundle(lastLang, 'translation');
|
|
540
|
-
}
|
|
541
|
-
i18n.addResourceBundle(lang, 'translation', trs);
|
|
542
|
-
}
|
|
543
|
-
}, [translations]);
|
|
544
|
-
|
|
545
|
-
const changeLanguage = (newLang) => {
|
|
546
|
-
if (typeof(newLang) === 'string' && newLang) {
|
|
547
|
-
|
|
548
|
-
i18n.changeLanguage(newLang, (err)=>{
|
|
549
|
-
|
|
550
|
-
i18next.removeResourceBundle(lang, "dataEngineTranslations");
|
|
551
|
-
const trs = {...websiteTranslations[newLang]?.['translation']} || {};
|
|
552
|
-
i18next.addResourceBundle(newLang, 'dataEngineTranslations', trs);
|
|
553
|
-
|
|
554
|
-
gtag("event", "change_language "+newLang);
|
|
555
|
-
});
|
|
556
|
-
}
|
|
507
|
+
function App() {
|
|
508
|
+
// On gère le queryClient dans un état pour pouvoir le réinitialiser complètement.
|
|
509
|
+
const [queryClient, setQueryClient] = useState(() => new QueryClient());
|
|
510
|
+
const [refreshKey, setRefreshKey] = useState(0);
|
|
511
|
+
|
|
512
|
+
// La fonction qui va créer une NOUVELLE instance du client.
|
|
513
|
+
const resetQueryClient = () => {
|
|
514
|
+
setQueryClient(new QueryClient());
|
|
515
|
+
setRefreshKey(key => key + 1); // On incrémente la clé pour forcer le re-montage.
|
|
557
516
|
};
|
|
558
517
|
|
|
559
|
-
|
|
560
|
-
changeLanguage(lang);
|
|
561
|
-
}, [lang]);
|
|
562
|
-
|
|
563
|
-
return <Layout refreshUI={refreshUI} header={<header className={"flex"}>
|
|
564
|
-
<Tooltip id={"header"}/>
|
|
565
|
-
<h1 className="flex-1">{seoTitle}</h1>
|
|
566
|
-
<div className="center">
|
|
567
|
-
<SelectField label={<FaLanguage />} items={allLangs} onChange={(e) => {
|
|
568
|
-
changeLanguage(e.value);
|
|
569
|
-
}} />
|
|
570
|
-
</div>
|
|
571
|
-
<div className="flex">
|
|
572
|
-
<FaQuestion data-tooltip-id="header" data-tooltip-content="Documentation" onClick={()=> {
|
|
573
|
-
window.open("/en/documentation/", "_blank");
|
|
574
|
-
}} />
|
|
575
|
-
</div>
|
|
576
|
-
</header>} />
|
|
577
|
-
}
|
|
578
|
-
function App() {
|
|
518
|
+
const [refreshReducer, refreshUI]= useReducer((n) => n+1, 0,() => 0);
|
|
579
519
|
|
|
580
520
|
return (
|
|
581
521
|
<QueryClientProvider client={queryClient}>
|
|
582
|
-
<AuthProvider>
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
522
|
+
<AuthProvider key={refreshKey}>
|
|
523
|
+
<CookiesProvider>
|
|
524
|
+
<ModelProvider key={refreshKey}>
|
|
525
|
+
<BrowserRouter>
|
|
526
|
+
<UIProvider>
|
|
527
|
+
<NotificationProvider>
|
|
528
|
+
<CommandProvider onResetQueryClient={resetQueryClient}>
|
|
529
|
+
<BaseLayout onResetQueryClient={resetQueryClient} refreshUI={refreshUI} />
|
|
530
|
+
</CommandProvider>
|
|
531
|
+
</NotificationProvider>
|
|
532
|
+
</UIProvider>
|
|
533
|
+
</BrowserRouter>
|
|
534
|
+
</ModelProvider>
|
|
535
|
+
</CookiesProvider>
|
|
536
|
+
</AuthProvider>
|
|
595
537
|
</QueryClientProvider>
|
|
596
538
|
);
|
|
597
539
|
}
|