data-primals-engine 1.5.0 → 1.5.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 +35 -0
- package/client/src/AddWidgetTypeModal.jsx +47 -43
- package/client/src/App.jsx +2 -6
- package/client/src/App.scss +12 -0
- package/client/src/AssistantChat.jsx +363 -323
- package/client/src/AssistantChat.scss +27 -10
- package/client/src/Dashboard.jsx +480 -396
- package/client/src/Dashboard.scss +1 -1
- package/client/src/DashboardHtmlViewItem.jsx +147 -0
- package/client/src/DashboardView.jsx +654 -569
- package/client/src/DataEditor.jsx +10 -3
- package/client/src/DataLayout.jsx +805 -755
- package/client/src/DataLayout.scss +14 -0
- package/client/src/DataTable.jsx +39 -75
- package/client/src/Dialog.scss +1 -1
- package/client/src/Field.jsx +2057 -1825
- package/client/src/FlexViewCard.jsx +44 -0
- package/client/src/HistoryDialog.jsx +47 -14
- package/client/src/HtmlViewBuilderModal.jsx +91 -0
- package/client/src/HtmlViewBuilderModal.scss +18 -0
- package/client/src/HtmlViewCard.jsx +44 -0
- package/client/src/HtmlViewCard.scss +35 -0
- package/client/src/KanbanCard.jsx +1 -2
- package/client/src/ModelCreator.jsx +5 -4
- package/client/src/ModelCreatorField.jsx +51 -4
- package/client/src/ModelList.jsx +92 -53
- package/client/src/Notification.jsx +136 -136
- package/client/src/Notification.scss +0 -18
- package/client/src/Pagination.jsx +5 -3
- package/client/src/RelationField.jsx +354 -258
- package/client/src/RelationSelectorWidget.jsx +173 -0
- package/client/src/contexts/ModelContext.jsx +10 -1
- package/client/src/contexts/UIContext.jsx +72 -63
- package/client/src/filter.js +262 -212
- package/client/src/hooks/useValidation.js +75 -0
- package/client/src/translations.js +24 -24
- package/package.json +2 -1
- package/src/constants.js +1 -1
- package/src/defaultModels.js +1596 -1544
- package/src/i18n.js +710 -10
- package/src/modules/assistant/assistant.js +148 -18
- package/src/modules/bucket.js +2 -1
- package/src/modules/data/data.core.js +118 -92
- package/src/modules/data/data.history.js +531 -492
- package/src/modules/data/data.js +3 -53
- package/src/modules/data/data.operations.js +77 -26
- package/src/modules/data/data.relations.js +686 -686
- package/src/modules/data/data.routes.js +1879 -1821
- package/src/modules/data/data.validation.js +81 -2
- package/src/modules/file.js +247 -238
- package/src/packs.js +5482 -5478
- package/test/data.integration.test.js +1115 -1060
package/client/src/DataTable.jsx
CHANGED
|
@@ -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
|
|
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,
|
|
265
|
-
|
|
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
|
|
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">{
|
|
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
|
-
|
|
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
|
-
|
|
553
|
-
if (
|
|
554
|
-
|
|
555
|
-
|
|
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(
|
|
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
|
|
564
|
-
onChange={(
|
|
565
|
-
|
|
566
|
-
|
|
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
|
-
|
|
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.
|
|
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].
|
|
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
|
|
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>
|