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.
Files changed (70) hide show
  1. package/README.md +938 -915
  2. package/client/README.md +136 -136
  3. package/client/index.js +0 -1
  4. package/client/public/doc/API.postman_collection.json +213 -213
  5. package/client/public/react.svg +9 -9
  6. package/client/src/AddWidgetTypeModal.jsx +47 -47
  7. package/client/src/App.css +42 -42
  8. package/client/src/App.jsx +92 -150
  9. package/client/src/App.scss +6 -0
  10. package/client/src/AssistantChat.jsx +362 -363
  11. package/client/src/Button.jsx +12 -12
  12. package/client/src/Dashboard.jsx +480 -480
  13. package/client/src/DashboardHtmlViewItem.jsx +146 -146
  14. package/client/src/DashboardView.jsx +654 -654
  15. package/client/src/DataEditor.jsx +383 -383
  16. package/client/src/DataLayout.jsx +779 -808
  17. package/client/src/DataTable.jsx +782 -822
  18. package/client/src/DataTable.scss +186 -186
  19. package/client/src/GeolocationField.jsx +93 -93
  20. package/client/src/HistoryDialog.jsx +307 -307
  21. package/client/src/HistoryDialog.scss +319 -319
  22. package/client/src/HtmlViewBuilderModal.jsx +90 -90
  23. package/client/src/HtmlViewBuilderModal.scss +17 -17
  24. package/client/src/HtmlViewCard.jsx +43 -43
  25. package/client/src/ModelCreator.jsx +683 -686
  26. package/client/src/ModelCreator.scss +1 -1
  27. package/client/src/ModelCreatorField.jsx +950 -950
  28. package/client/src/ModelImporter.jsx +3 -0
  29. package/client/src/ModelList.jsx +280 -280
  30. package/client/src/Notification.jsx +136 -136
  31. package/client/src/PackGallery.jsx +391 -391
  32. package/client/src/PackGallery.scss +231 -231
  33. package/client/src/Pagination.jsx +143 -143
  34. package/client/src/RelationField.jsx +354 -354
  35. package/client/src/RelationSelectorWidget.jsx +172 -172
  36. package/client/src/RelationValue.jsx +2 -1
  37. package/client/src/contexts/CommandContext.jsx +260 -0
  38. package/client/src/contexts/ModelContext.jsx +4 -1
  39. package/client/src/contexts/UIContext.jsx +72 -72
  40. package/client/src/filter.js +263 -263
  41. package/client/src/index.css +90 -90
  42. package/package.json +6 -5
  43. package/perf/artillery-hooks.js +37 -37
  44. package/perf/setup.yml +25 -25
  45. package/src/constants.js +4 -0
  46. package/src/defaultModels.js +7 -0
  47. package/src/engine.js +335 -335
  48. package/src/events.js +232 -137
  49. package/src/filter.js +274 -274
  50. package/src/index.js +1 -0
  51. package/src/modules/assistant/assistant.js +225 -83
  52. package/src/modules/auth-google/index.js +50 -50
  53. package/src/modules/auth-microsoft/index.js +81 -81
  54. package/src/modules/data/data.core.js +118 -118
  55. package/src/modules/data/data.history.js +555 -555
  56. package/src/modules/data/data.operations.js +112 -9
  57. package/src/modules/data/data.relations.js +686 -686
  58. package/src/modules/data/data.routes.js +1879 -1879
  59. package/src/modules/data/data.validation.js +2 -2
  60. package/src/modules/file.js +247 -247
  61. package/src/modules/user.js +14 -9
  62. package/src/packs.js +2 -2
  63. package/src/providers.js +2 -2
  64. package/src/services/stripe.js +196 -196
  65. package/src/sso.js +193 -193
  66. package/test/data.history.integration.test.js +264 -264
  67. package/test/data.integration.test.js +1281 -1206
  68. package/test/events.test.js +108 -10
  69. package/test/model.integration.test.js +377 -377
  70. package/test/user.test.js +106 -1
@@ -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
- function Layout ({header, routes, body, footer,refreshUI}) {
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
- selector: '.tourStep-create-model', // Sélecteur CSS de l'élément
423
- content: t('tourpoint.createModel'),
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
- selector: '.tourStep-tutorials',
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
- selector: '.model-list [data-testid=model_budget]', // Example target, adjust as needed
452
- content: t('tourpoint.model-list-budget', 'Gérez votre budget et votre portefeuille avec précision !'),
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
- // ... steps specific to the engineer profile, focusing on workflow ...
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 &quot;{params.user}&quot; 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 &quot;{params.user}&quot; pour accéder à vos données</p>}</>;
515
505
  }
516
506
 
517
- const allLangs = Object.keys(langs).map(l => {
518
- return {value: l, label: langs[l]};
519
- })
520
-
521
- const BaseLayout=()=>{
522
- const [lastLang, setLastLang] = useState(null);
523
- const { i18n, t} = useTranslation();
524
- const lang = (i18n.resolvedLanguage || i18n.language).split(/[-_]/)?.[0];
525
- const { me, setMe } = useAuthContext();
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
- useEffect(() => {
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
- <CookiesProvider>
584
- <ModelProvider>
585
- <BrowserRouter>
586
- <UIProvider>
587
- <NotificationProvider>
588
- <BaseLayout></BaseLayout>
589
- </NotificationProvider>
590
- </UIProvider>
591
- </BrowserRouter>
592
- </ModelProvider>
593
- </CookiesProvider>
594
- </AuthProvider>
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
  }
@@ -854,6 +854,12 @@ a:hover {
854
854
  .mg-2 {
855
855
  margin: 16px;
856
856
  }
857
+ .mg-3 {
858
+ margin: 24px;
859
+ }
860
+ .mg-4 {
861
+ margin: 32px;
862
+ }
857
863
 
858
864
  span.required {
859
865
  color: red;