data-primals-engine 1.5.0 → 1.5.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.
Files changed (63) hide show
  1. package/README.md +37 -0
  2. package/client/src/AddWidgetTypeModal.jsx +47 -43
  3. package/client/src/App.jsx +2 -6
  4. package/client/src/App.scss +13 -1
  5. package/client/src/AssistantChat.jsx +363 -323
  6. package/client/src/AssistantChat.scss +27 -10
  7. package/client/src/Dashboard.jsx +480 -396
  8. package/client/src/Dashboard.scss +1 -1
  9. package/client/src/DashboardHtmlViewItem.jsx +147 -0
  10. package/client/src/DashboardView.jsx +654 -569
  11. package/client/src/DataEditor.jsx +10 -3
  12. package/client/src/DataLayout.jsx +807 -755
  13. package/client/src/DataLayout.scss +14 -0
  14. package/client/src/DataTable.jsx +39 -75
  15. package/client/src/Dialog.scss +1 -1
  16. package/client/src/Field.jsx +2057 -1825
  17. package/client/src/FlexViewCard.jsx +44 -0
  18. package/client/src/HistoryDialog.jsx +69 -14
  19. package/client/src/HtmlViewBuilderModal.jsx +91 -0
  20. package/client/src/HtmlViewBuilderModal.scss +18 -0
  21. package/client/src/HtmlViewCard.jsx +44 -0
  22. package/client/src/HtmlViewCard.scss +35 -0
  23. package/client/src/KanbanCard.jsx +1 -2
  24. package/client/src/ModelCreator.jsx +5 -4
  25. package/client/src/ModelCreatorField.jsx +51 -4
  26. package/client/src/ModelList.jsx +280 -236
  27. package/client/src/Notification.jsx +136 -136
  28. package/client/src/Notification.scss +0 -18
  29. package/client/src/Pagination.jsx +5 -3
  30. package/client/src/RelationField.jsx +354 -258
  31. package/client/src/RelationSelectorWidget.jsx +173 -0
  32. package/client/src/contexts/ModelContext.jsx +10 -1
  33. package/client/src/contexts/UIContext.jsx +72 -63
  34. package/client/src/filter.js +263 -212
  35. package/client/src/hooks/useValidation.js +75 -0
  36. package/client/src/translations.js +24 -24
  37. package/package.json +7 -6
  38. package/src/constants.js +1 -1
  39. package/src/core.js +8 -1
  40. package/src/defaultModels.js +1596 -1544
  41. package/src/engine.js +85 -43
  42. package/src/events.js +137 -113
  43. package/src/i18n.js +710 -10
  44. package/src/index.js +3 -0
  45. package/src/modules/assistant/assistant.js +253 -134
  46. package/src/modules/assistant/constants.js +2 -1
  47. package/src/modules/bucket.js +2 -1
  48. package/src/modules/data/data.core.js +118 -92
  49. package/src/modules/data/data.history.js +555 -492
  50. package/src/modules/data/data.js +3 -53
  51. package/src/modules/data/data.operations.js +3381 -3231
  52. package/src/modules/data/data.relations.js +686 -686
  53. package/src/modules/data/data.routes.js +1879 -1821
  54. package/src/modules/data/data.validation.js +81 -2
  55. package/src/modules/file.js +247 -238
  56. package/src/modules/user.js +1 -0
  57. package/src/modules/workflow.js +2 -2
  58. package/src/openai.jobs.js +3 -2
  59. package/src/packs.js +5482 -5478
  60. package/src/sso.js +2 -2
  61. package/src/workers/import-export-worker.js +1 -1
  62. package/test/data.history.integration.test.js +264 -192
  63. package/test/data.integration.test.js +149 -3
@@ -0,0 +1,14 @@
1
+
2
+
3
+ /* website*/
4
+ #content .datalayout {
5
+ padding: 0;
6
+
7
+ h1 {
8
+ margin: 24px 8px;
9
+ }
10
+
11
+ h2 {
12
+ margin: 20px 4px 0 4px;
13
+ }
14
+ }
@@ -50,10 +50,8 @@ import {Zoom} from "yet-another-react-lightbox/plugins";
50
50
  import Lightbox from "yet-another-react-lightbox";
51
51
 
52
52
  import "./DataTable.scss"
53
- import useLocalStorage from "./hooks/useLocalStorage.js";
54
53
  import TutorialsMenu from "../src/TutorialsMenu.jsx";
55
54
  import {useTutorials} from "./hooks/useTutorials.jsx";
56
- import PackGallery from "./PackGallery.jsx";
57
55
  import {HiddenableCell} from "./HiddenableCell.jsx";
58
56
  import ConditionBuilder from "./ConditionBuilder.jsx";
59
57
  import {pagedFilterToMongoConds} from "./filter.js";
@@ -73,7 +71,8 @@ const Header = ({
73
71
  onChangeFilterValue,
74
72
  setFilterValues,
75
73
  filterValues,
76
- advanced=true
74
+ advanced=true,
75
+ selectionMode=false
77
76
  }) => {
78
77
 
79
78
  const {t} = useTranslation()
@@ -111,8 +110,8 @@ const Header = ({
111
110
  {advanced && (<th className={"mini"}>
112
111
  <div className="flex flex-row">
113
112
 
114
- <Button onClick={handleFilter} className={iconFilterActive ? ' active' : ''}><FaFilter/></Button>
115
- {filterActive && <Button onClick={() => handleAdvancedFilter()}><FaWrench /></Button>}
113
+ <Button type={"button"} onClick={handleFilter} className={iconFilterActive ? ' active' : ''}><FaFilter/></Button>
114
+ {filterActive && <Button type={"button"} onClick={() => handleAdvancedFilter()}><FaWrench /></Button>}
116
115
  <CheckboxField checkbox={true} checked={checkedItems?.length === data.length} onChange={e => {
117
116
  if (checkedItems?.length === data.length) {
118
117
  setCheckedItems([]);
@@ -132,7 +131,7 @@ const Header = ({
132
131
  setPagedFilters(pagedFilters => ({
133
132
  ...pagedFilters,
134
133
  [model.name]: c || pagedFilters[model.name] || {} }));
135
- }} initialValue={{ $and: pagedFilterToMongoConds(pagedFilters, model)}} models={models} model={model.name} checkedItems={checkedItems} setCheckedItems={setCheckedItems} data={data} filterActive={filterActive} onChangeFilterValue={onChangeFilterValue} setFilterValues={setFilterValues}/>
134
+ }} initialValue={{ $and: pagedFilterToMongoConds(pagedFilters, model)}} models={models} model={model} checkedItems={checkedItems} setCheckedItems={setCheckedItems} data={data} filterActive={filterActive} onChangeFilterValue={onChangeFilterValue} setFilterValues={setFilterValues}/>
136
135
  <Button onClick={() =>{
137
136
  setPagedFilters(pagedFilters => ({
138
137
  ...pagedFilters,
@@ -159,11 +158,11 @@ const Header = ({
159
158
  </th>;
160
159
 
161
160
  totalCol++;
162
- return <FilterField advanced={advanced} reversed={reversed} filterValues={filterValues} setFilterValues={setFilterValues}
161
+ return <FilterField key={"datatable-th-"+model.name+":"+field.name} advanced={advanced} reversed={reversed} filterValues={filterValues} setFilterValues={setFilterValues}
163
162
  model={model} field={field} active={filterActive}
164
163
  onChangeFilterValue={onChangeFilterValue}/>;
165
164
  })}
166
- {advanced && (<th><Trans i18nKey="actions">Actions</Trans>
165
+ {advanced && !selectionMode && (<th><Trans i18nKey="actions">Actions</Trans>
167
166
  {Object.keys(pagedFilters[model.name] || {})
168
167
  .filter(f=> Object.keys(pagedFilters[model.name]?.[f] || {}).length > 0).length > 0 && <div>
169
168
  <button onClick={() => {
@@ -261,9 +260,9 @@ export function DataTable({
261
260
  onShowAPI,
262
261
  filterValues,
263
262
  setFilterValues = () => {},
264
- data: propData, // NOUVEAU: Accepter les données via les props
265
- advanced=true
266
-
263
+ data: propData,
264
+ advanced= true,
265
+ selectionMode= false
267
266
  }) {
268
267
  const {
269
268
  models,
@@ -287,20 +286,9 @@ export function DataTable({
287
286
  const isDataLoaded = true;
288
287
  const [importVisible, setImportVisible] = useState(false);
289
288
  const [filterActive, setFilterActive] = useState(false)
290
-
291
- const [showPackGallery, setShowPackGallery] = useState(false);
292
289
  const [showExportDialog, setExportDialogVisible] = useState(false);
293
290
 
294
-
295
291
  const [selectedRow, setSelectedRow] = useState(null);
296
-
297
- const [showAddPack, setAddPackVisible] = useState(false);
298
- const handleAddPack = () => {
299
- setAddPackVisible(!showAddPack);
300
- }
301
- const handleShowPacks = () => {
302
- setShowPackGallery(true);
303
- };
304
292
  const handleDelete = async (item) => {
305
293
  if (model && item && item._id) { // Assurez-vous d'avoir un identifiant unique pour chaque élément
306
294
  try {
@@ -466,13 +454,9 @@ export function DataTable({
466
454
  const [lightboxIndex, setLightboxIndex] = useState(0);
467
455
  const [lightboxOpened, setLightboxOpened] = useState(false);
468
456
  const [lightboxSlides, setLightboxSlides] = useState([]);
469
- const [tutorialDialogVisible, setTutorialDialogVisible] = useState(false);
470
457
 
471
458
  const [newPackName, setNewPackName] = useState(''); // pack
472
459
 
473
- const handleShowTutorialMenu = () => {
474
- setTutorialDialogVisible(!tutorialDialogVisible);
475
- }
476
460
 
477
461
  if (!model)
478
462
  return <></>;
@@ -493,38 +477,23 @@ export function DataTable({
493
477
 
494
478
  onDuplicateData(dataToDuplicate);
495
479
  };
480
+
481
+ const desc = t(`model_description_${selectedModel.name}`, selectedModel.description);
496
482
  return (
497
483
  <div className={`datatable${filterActive ? ' filter-active' : ''}`}>
498
- {advanced && <div className="flex actions flex-left">
484
+ {advanced && !selectionMode && <div className="flex actions">
499
485
  {t(`model_${selectedModel?.name}`, selectedModel?.name) !== selectedModel?.name && (
500
486
  <span className="badge"><strong>model</strong> : {selectedModel?.name}</span>)}
501
487
  {selectedModel.name === 'dashboard' && <Button className={"btn"} onClick={() => {
502
488
  nav('/user/'+getUserHash(me)+'/dashboards');
503
489
  }}><FaEye /> Tableaux de bord</Button> }
504
- <p className="model-desc hint">{t(`model_description_${selectedModel.name}`, selectedModel.description)}</p>
505
- <Button onClick={() => onAddData(model)}><FaPlus/><Trans i18nKey="btns.addData">Ajouter une
506
- donnée</Trans></Button>
490
+ {desc && <p className="model-desc hint">{desc}</p>}
507
491
  <Button onClick={handleImport} title={t("btns.import")}><FaFileImport/><Trans
508
492
  i18nKey="btns.import">Importer</Trans></Button>
509
- <Button disabled={isLoading} onClick={handleExport} title={t("btns.export")}><FaFileExport/><Trans
510
- i18nKey="btns.export">Exporter</Trans></Button>
511
- <Button className="tourStep-import-datapack" onClick={handleShowPacks} title={t("btns.addPack")}><FaPlus/><Trans
512
- i18nKey="btns.addPack">Packs...</Trans></Button>
513
-
514
- <Button className={"tourStep-tutorials " + (/^demo[0-9]{1,2}$/.test(me.username) ? "btn-primary" : "btn")} onClick={handleShowTutorialMenu} title={t("btns.showTutos")}><FaPlus/><Trans
515
- i18nKey="btns.showTutos">Tutoriels</Trans></Button>
493
+ <Button disabled={isLoading} onClick={handleExport} title={t("btns.export")}><FaFileExport/><Trans i18nKey="btns.export">Exporter</Trans></Button>
516
494
  {!/^demo[0-9]{1,2}$/.test(me.username) && (<Button onClick={handleBackup} title={t("btns.backup")}><FaDatabase/><Trans
517
495
  i18nKey="btns.backup">Backup</Trans></Button>)}
518
- <DialogProvider>
519
- {tutorialDialogVisible && (
520
- <Dialog isClosable={true} isModal={true} onClose={() => setTutorialDialogVisible(false)}>
521
- <TutorialsMenu />
522
- </Dialog>
523
- )}
524
- </DialogProvider>
525
- <Button className="btn btn-primary" onClick={() => {
526
- onShowAPI(selectedModel);
527
- }}><FaBook/> {t('btns.api', 'API')}</Button>
496
+
528
497
  </div>}
529
498
  <div className={"table-wrapper"}>
530
499
  <Tooltip id={"tooltipFile"} clickable={true} />
@@ -543,32 +512,29 @@ export function DataTable({
543
512
  {isDataLoaded && (
544
513
  <table>
545
514
  <thead>
546
- <Header advanced={advanced} model={model} setCheckedItems={setCheckedItems} filterValues={filterValues} data={data} setFilterValues={setFilterValues} onChangeFilterValue={onChangeFilterValue} checkedItems={checkedItems} filterActive={filterActive} handleFilter={handleFilter}/>
515
+ <Header advanced={advanced} model={model} setCheckedItems={setCheckedItems} filterValues={filterValues} data={data} setFilterValues={setFilterValues} onChangeFilterValue={onChangeFilterValue} checkedItems={checkedItems} filterActive={filterActive} handleFilter={handleFilter} selectionMode={selectionMode}/>
547
516
  </thead>
548
517
  <tbody>
549
518
  {(data || []).map((item) => (
550
519
  <><DialogProvider>
551
520
  <tr key={item._id} onDoubleClick={() => onEdit(item)} onClick={(e) => {
552
- const checked = (!e.target.closest('tr').querySelector('td:first-child input')?.checked);
553
- if (checked) {
554
- setCheckedItems(items => {
555
- return [...items, item];
556
- });
521
+ // Empêche le déclenchement du clic sur la ligne si on clique sur un bouton ou un lien
522
+ if (e.target.closest('button, a')) {
523
+ return;
524
+ }
525
+ const isCurrentlyChecked = checkedItems?.some(i => i?._id === item._id);
526
+ if (!isCurrentlyChecked) {
527
+ setCheckedItems([...(checkedItems || []), item]);
557
528
  } else {
558
- setCheckedItems(items => items.filter(i => i._id !== item._id));
529
+ setCheckedItems((checkedItems || []).filter(i => i._id !== item._id));
559
530
  }
560
531
  }}>
561
532
  {advanced && (<td className={"mini"}>
562
533
  <CheckboxField checkbox={true} className={"input-ref"}
563
- checked={checkedItems?.some(i => i._id === item._id)}
564
- onChange={(e) => {
565
- if (e) {
566
- setCheckedItems(items => {
567
- return [...items, item];
568
- });
569
- } else {
570
- setCheckedItems(items => items.filter(i => i._id !== item._id));
571
- }
534
+ checked={checkedItems?.some(i => i?._id === item._id)}
535
+ onChange={() => {
536
+ // La logique est gérée par le onClick de la <tr>
537
+ // pour permettre de cliquer n'importe où sur la ligne.
572
538
  }}/></td>)}
573
539
  {(model?.fields ||[]).map(field => {
574
540
 
@@ -641,7 +607,6 @@ export function DataTable({
641
607
  }
642
608
  if (field.type === 'geolocation') {
643
609
  const geoData = item[field.name];
644
- console.log({geoData})
645
610
  if (geoData && geoData.coordinates && geoData.coordinates.length === 2) {
646
611
  const [lng, lat] = geoData.coordinates;
647
612
  const coordinatesText = `${lat.toFixed(4)}, ${lng.toFixed(4)}`;
@@ -673,14 +638,18 @@ export function DataTable({
673
638
  })));
674
639
  e.preventDefault();
675
640
  };
676
- t = (item[field.name] || []).map((it,i) => {
641
+
642
+ if( !Array.isArray(item[field.name]))
643
+ return <td key={field.name}>
644
+ </td>
645
+ t = (item[field.name] ||[]).map((it,i) => {
677
646
 
678
647
  const r = `
679
648
  <strong>filename</strong> : ${it.filename}<br />
680
649
  <strong>guid</strong> : ${it.guid}<br />
681
650
  <strong>type</strong> : ${it.mimeType}<br />
682
651
  <strong>size</strong> : ${it.size} bytes<br />
683
- <strong>timestamp</strong> : ${it.timestamp ? new Date(it.timestamp).toLocaleString(lang) : ''}
652
+ <strong>timestamp</strong> : ${it.createdAt ? new Date(it.createdAts).toLocaleString(lang) : ''}
684
653
  `;
685
654
  return <a key={it.guid} href={`/resources/${it.guid}`} target="_blank"
686
655
  data-tooltip-id={"tooltipFile"} data-tooltip-html={r}
@@ -715,7 +684,7 @@ export function DataTable({
715
684
  <strong>guid</strong> : ${item[field.name].guid}<br />
716
685
  <strong>type</strong> : ${item[field.name].mimeType}<br />
717
686
  <strong>size</strong> : ${item[field.name].size} bytes<br />
718
- <strong>timestamp</strong> : ${item[field.name].timestamp.toLocaleString(lang)}
687
+ <strong>timestamp</strong> : ${new Date(item[field.name].createdAt)?.toLocaleString(lang)}
719
688
  `;
720
689
  if (['image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/svg+xml', 'image/webp', 'image/bmp', 'image/tiff', 'image/x-icon', 'image/x-windows-bmp'].includes(item[field.name].mimeType))
721
690
  return <td key={field.name}>{hiddenable(<a
@@ -789,7 +758,7 @@ export function DataTable({
789
758
  return <td key={field.name} className={isLightColor(field.color)?"lighted":""} style={{backgroundColor: field.color, color: isLightColor(field.color) ? 'black': '#E3E3E3'}}>
790
759
  {hiddenable(item[field.name])}</td>;
791
760
  })}
792
- {advanced && (<td>
761
+ {advanced && !selectionMode && (<td>
793
762
  <button data-tooltip-id="tooltipActions"
794
763
  data-tooltip-content={t('btns.edit', 'Modifier')}
795
764
  onClick={() => handleEdit(item)}><FaPencil/></button>
@@ -821,7 +790,7 @@ export function DataTable({
821
790
  <tfoot>
822
791
  {data.length > 10 && (<Header advanced={advanced} reversed={true} model={model} setCheckedItems={setCheckedItems}
823
792
  filterValues={filterValues} data={data}
824
- setFilterValues={setFilterValues}
793
+ setFilterValues={setFilterValues} selectionMode={selectionMode}
825
794
  onChangeFilterValue={onChangeFilterValue}
826
795
  checkedItems={checkedItems} filterActive={filterActive} handleFilter={handleFilter}/>)}
827
796
  </tfoot>
@@ -843,14 +812,9 @@ export function DataTable({
843
812
  />
844
813
  <ExportDialog isOpen={showExportDialog} onClose={() => {
845
814
  setExportDialogVisible(false);
846
- }} availableModels={models} currentModel={selectedModel.name} hasSelection={true} onExport={(data)=>{
815
+ }} availableModels={models} currentModel={selectedModel?.name} hasSelection={true} onExport={(data)=>{
847
816
  exportMutation(data);
848
817
  }} />
849
- {showPackGallery && (
850
- <Dialog isClosable={true} isModal={true} onClose={() => setShowPackGallery(false)}>
851
- <PackGallery />
852
- </Dialog>
853
- )}
854
818
  </DialogProvider>
855
819
  </div>
856
820
  </div>
@@ -73,7 +73,7 @@
73
73
  word-wrap: break-word;
74
74
  max-height: 85vh;
75
75
  overflow-y: auto;
76
- > .field {
76
+ .field {
77
77
  margin: 0;
78
78
  }
79
79
  }