data-primals-engine 1.0.10 → 1.1.0
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/client/README.md +8 -0
- package/client/index.html +31 -0
- package/client/index.js +25 -0
- package/client/package-lock.json +11664 -0
- package/client/package.json +70 -0
- package/client/public/demo/26899917-d1ba-4df4-bb33-48d09a8778da.jpg +0 -0
- package/client/public/demo/4b9894c7-12cd-466d-8fd3-a2757b09ec96.jpg +0 -0
- package/client/public/demo/7ed369ac-a1a2-4b45-b0cd-4fd5bcf5a75a.jpg +0 -0
- package/client/public/doc/API.postman_collection.json +214 -0
- package/client/public/github.svg +1 -0
- package/client/public/profilCompany.jpg +0 -0
- package/client/public/profilDeveloper.jpg +0 -0
- package/client/public/profilEngineer.jpg +0 -0
- package/client/public/profilPersonal.jpg +0 -0
- package/client/public/profils .psd +0 -0
- package/client/public/react.svg +9 -0
- package/client/src/APIInfo.jsx +328 -0
- package/client/src/AddWidgetTypeModal.jsx +44 -0
- package/client/src/AddWidgetTypeModal.scss +87 -0
- package/client/src/App.css +42 -0
- package/client/src/App.jsx +586 -0
- package/client/src/App.scss +1466 -0
- package/client/src/AssistantChat.jsx +280 -0
- package/client/src/AssistantChat.scss +229 -0
- package/client/src/Button.jsx +12 -0
- package/client/src/Button.scss +197 -0
- package/client/src/CalculationBuilder.jsx +338 -0
- package/client/src/CalculationStepRow.jsx +199 -0
- package/client/src/CalendarConfigModal.jsx +49 -0
- package/client/src/ChartConfigModal.jsx +402 -0
- package/client/src/ConditionBuilder.jsx +622 -0
- package/client/src/ConditionBuilder.scss +672 -0
- package/client/src/ConditionBuilder2.jsx +757 -0
- package/client/src/ContentView.jsx +274 -0
- package/client/src/CronBuilder.jsx +126 -0
- package/client/src/CronBuilder.scss +128 -0
- package/client/src/CronPartBuilder.jsx +150 -0
- package/client/src/Dashboard.jsx +210 -0
- package/client/src/Dashboard.scss +445 -0
- package/client/src/DashboardChart.jsx +362 -0
- package/client/src/DashboardFlexViewItem.jsx +129 -0
- package/client/src/DashboardView.jsx +554 -0
- package/client/src/DataChart.jsx +349 -0
- package/client/src/DataEditor.jsx +556 -0
- package/client/src/DataLayout.jsx +709 -0
- package/client/src/DataTable.jsx +996 -0
- package/client/src/DataTable.scss +73 -0
- package/client/src/Dialog.jsx +90 -0
- package/client/src/Dialog.scss +117 -0
- package/client/src/DisplayFlexNodeRenderer.jsx +115 -0
- package/client/src/DocumentationPageLayout.jsx +93 -0
- package/client/src/DocumentationPageLayout.scss +262 -0
- package/client/src/Draggable.jsx +222 -0
- package/client/src/Draggable.scss +49 -0
- package/client/src/ExportDialog.jsx +172 -0
- package/client/src/Field.jsx +1558 -0
- package/client/src/FlexBuilder.jsx +269 -0
- package/client/src/FlexBuilder.scss +226 -0
- package/client/src/FlexBuilderControls.jsx +170 -0
- package/client/src/FlexBuilderModal.jsx +75 -0
- package/client/src/FlexBuilderModal.scss +109 -0
- package/client/src/FlexBuilderPreview.jsx +48 -0
- package/client/src/FlexBuilderUtils.js +84 -0
- package/client/src/FlexDataRenderer.jsx +170 -0
- package/client/src/FlexDataRenderer.scss +80 -0
- package/client/src/FlexNode.jsx +290 -0
- package/client/src/FlexTreeUtils.js +129 -0
- package/client/src/HiddenableCell.jsx +46 -0
- package/client/src/HiddenableCell.scss +36 -0
- package/client/src/KPIDialog.jsx +39 -0
- package/client/src/KPIWidget.jsx +139 -0
- package/client/src/KanbanCard.jsx +92 -0
- package/client/src/KanbanColumn.jsx +29 -0
- package/client/src/KanbanConfigModal.jsx +115 -0
- package/client/src/KanbanView.jsx +113 -0
- package/client/src/KanbanView.scss +107 -0
- package/client/src/MessageRotator.jsx +77 -0
- package/client/src/MessageRotator.scss +14 -0
- package/client/src/ModelCreator.jsx +640 -0
- package/client/src/ModelCreator.scss +154 -0
- package/client/src/ModelCreatorField.jsx +810 -0
- package/client/src/ModelImporter.jsx +120 -0
- package/client/src/ModelList.jsx +205 -0
- package/client/src/Notification.jsx +137 -0
- package/client/src/Notification.scss +126 -0
- package/client/src/NotificationProvider.jsx +73 -0
- package/client/src/PackGallery.jsx +211 -0
- package/client/src/PackGallery.scss +156 -0
- package/client/src/Pagination.jsx +141 -0
- package/client/src/RTE.jsx +415 -0
- package/client/src/RTETrans.jsx +103 -0
- package/client/src/RelationField.jsx +247 -0
- package/client/src/RelationValue.jsx +230 -0
- package/client/src/RestoreConfirmationModal.jsx +183 -0
- package/client/src/RestoreDialog.jsx +134 -0
- package/client/src/RestoreDialog.scss +67 -0
- package/client/src/RichTextEditorModal.jsx +34 -0
- package/client/src/RichTextEditorModal.scss +54 -0
- package/client/src/TourSpotlight.jsx +234 -0
- package/client/src/TourSpotlight.scss +100 -0
- package/client/src/TutorialsMenu.jsx +83 -0
- package/client/src/TutorialsMenu.scss +217 -0
- package/client/src/TutorialsRewardModal.jsx +65 -0
- package/client/src/Utils.jsx +78 -0
- package/client/src/ViewSwitcher.jsx +54 -0
- package/client/src/ViewSwitcher.scss +74 -0
- package/client/src/Webpage.jsx +69 -0
- package/client/src/_variables.scss +21 -0
- package/client/src/assets/react.svg +1 -0
- package/client/src/constants.js +68 -0
- package/client/src/contexts/AuthContext.jsx +28 -0
- package/client/src/contexts/ModelContext.jsx +328 -0
- package/client/src/contexts/UIContext.jsx +52 -0
- package/client/src/core/data.js +35 -0
- package/client/src/entry-client.jsx +15 -0
- package/client/src/entry-server.jsx +13 -0
- package/client/src/filter.js +222 -0
- package/client/src/hooks/data.js +101 -0
- package/client/src/hooks/useDebounce.js +20 -0
- package/client/src/hooks/useDragAndDrop.js +59 -0
- package/client/src/hooks/useLocalStorage.js +100 -0
- package/client/src/hooks/useMount.js +42 -0
- package/client/src/hooks/useTutorials.jsx +227 -0
- package/client/src/hooks/useWindowSize.js +24 -0
- package/client/src/i18n.js +47 -0
- package/client/src/index.css +90 -0
- package/client/src/index.js +10 -0
- package/client/src/main.jsx +11 -0
- package/client/src/translations.js +15940 -0
- package/client/src/tutorials.js +112 -0
- package/client/vite.config.js +30 -0
- package/eslint.config.js +50 -0
- package/package.json +48 -27
- package/server.js +9 -0
- package/src/constants.js +12 -23
- package/src/core.js +9 -8
- package/src/data.js +81 -80
- package/src/defaultModels.js +114 -115
- package/src/email.js +8 -9
- package/src/engine.js +5 -5
- package/src/events.js +1 -2
- package/src/i18n.js +5390 -211
- package/src/middlewares/middleware-mongodb.js +94 -93
- package/src/migrate.js +24 -24
- package/src/modules/assistant.js +55 -55
- package/src/modules/bucket.js +10 -9
- package/src/modules/data.js +387 -357
- package/src/modules/file.js +3 -6
- package/src/modules/mongodb.js +6 -7
- package/src/modules/user.js +24 -47
- package/src/modules/workflow.js +68 -67
- package/src/openai.jobs.js +2 -2
- package/src/packs.js +30 -31
- package/src/providers.js +101 -3
- package/src/setenv.js +1 -1
- package/src/user.js +0 -17
- package/src/workers/crypto-worker.js +21 -20
- package/src/workers/import-export-worker.js +18 -18
- package/test/data.backup.integration.test.js +7 -12
- package/test/data.integration.test.js +27 -25
- package/test/globalSetup.js +2 -2
- package/test/import_export.integration.test.js +6 -6
- package/test/model.integration.test.js +21 -22
- package/test/workflow.integration.test.js +2 -5
- package/test/workflow.robustness.test.js +6 -13
- package/vitest.config.js +2 -2
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/* client/src/index.css ou un fichier CSS dédié à DataImporter */
|
|
2
|
+
|
|
3
|
+
.import-progress-container {
|
|
4
|
+
margin-top: 20px;
|
|
5
|
+
padding: 15px;
|
|
6
|
+
border: 1px solid #ddd;
|
|
7
|
+
border-radius: 8px;
|
|
8
|
+
background-color: #f9f9f9;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.import-progress-container h3 {
|
|
12
|
+
margin-bottom: 15px;
|
|
13
|
+
color: #333;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.import-progress-container p {
|
|
17
|
+
margin-bottom: 8px;
|
|
18
|
+
color: #555;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.progress-bar-wrapper {
|
|
22
|
+
width: 100%;
|
|
23
|
+
background-color: #e0e0e0;
|
|
24
|
+
border-radius: 5px;
|
|
25
|
+
overflow: hidden;
|
|
26
|
+
margin-top: 15px;
|
|
27
|
+
height: 25px; /* Hauteur de la barre de progression */
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.progress-bar {
|
|
31
|
+
height: 100%;
|
|
32
|
+
background-color: #4CAF50; /* Couleur verte pour la progression */
|
|
33
|
+
text-align: center;
|
|
34
|
+
color: white;
|
|
35
|
+
line-height: 25px; /* Centrer le texte verticalement */
|
|
36
|
+
transition: width 0.5s ease-in-out; /* Transition douce pour l'animation de la barre */
|
|
37
|
+
border-radius: 5px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.import-errors {
|
|
41
|
+
margin-top: 20px;
|
|
42
|
+
padding: 10px;
|
|
43
|
+
border: 1px solid #f44336; /* Bordure rouge pour les erreurs */
|
|
44
|
+
background-color: #ffebee; /* Fond rouge clair */
|
|
45
|
+
border-radius: 5px;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.import-errors h4 {
|
|
49
|
+
color: #d32f2f; /* Rouge plus foncé pour le titre des erreurs */
|
|
50
|
+
margin-bottom: 10px;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.import-errors ul {
|
|
54
|
+
list-style-type: disc;
|
|
55
|
+
margin-left: 20px;
|
|
56
|
+
color: #c62828; /* Rouge pour les éléments de liste */
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.import-errors li {
|
|
60
|
+
margin-bottom: 5px;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* Ajustements pour le bouton de fermeture */
|
|
64
|
+
.flex.justify-end.mt-4 button {
|
|
65
|
+
padding: 8px 15px;
|
|
66
|
+
border-radius: 5px;
|
|
67
|
+
cursor: pointer;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.flex.justify-end.mt-4 button:disabled {
|
|
71
|
+
background-color: #cccccc;
|
|
72
|
+
cursor: not-allowed;
|
|
73
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import Button from "./Button.jsx";
|
|
2
|
+
import { FaWindowClose } from "react-icons/fa";
|
|
3
|
+
|
|
4
|
+
import "./Dialog.scss";
|
|
5
|
+
import { createContext, useContext, useEffect, useState } from "react";
|
|
6
|
+
import {useUI} from "./contexts/UIContext.jsx";
|
|
7
|
+
|
|
8
|
+
const DialogContext = createContext({});
|
|
9
|
+
|
|
10
|
+
export const useDialogContext = () => useContext(DialogContext);
|
|
11
|
+
|
|
12
|
+
export const DialogProvider = ({ children }) => {
|
|
13
|
+
const [dialogs, setDialogs] = useState(0);
|
|
14
|
+
const { locked, setLocked } = useUI()
|
|
15
|
+
const values = {
|
|
16
|
+
addDialog: () => setDialogs((dialogs) => dialogs + 1),
|
|
17
|
+
removeDialog: () =>
|
|
18
|
+
setDialogs((dialogs) => {
|
|
19
|
+
return dialogs > 0 ? dialogs - 1 : 0;
|
|
20
|
+
}),
|
|
21
|
+
};
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if( dialogs === 0){
|
|
24
|
+
setLocked(false);
|
|
25
|
+
}else{
|
|
26
|
+
setLocked(true);
|
|
27
|
+
}
|
|
28
|
+
}, [dialogs]);
|
|
29
|
+
return (
|
|
30
|
+
<DialogContext.Provider value={values}>
|
|
31
|
+
{dialogs > 0 ? (
|
|
32
|
+
<>
|
|
33
|
+
<div className="dialog-bg"></div>
|
|
34
|
+
{children}
|
|
35
|
+
</>
|
|
36
|
+
) : (
|
|
37
|
+
children
|
|
38
|
+
)}
|
|
39
|
+
</DialogContext.Provider>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
export const Dialog = ({
|
|
43
|
+
title,
|
|
44
|
+
children,
|
|
45
|
+
className,
|
|
46
|
+
onClose,
|
|
47
|
+
isClosable,
|
|
48
|
+
isModal,
|
|
49
|
+
}) => {
|
|
50
|
+
const [showModal, setModalVisible] = useState(true);
|
|
51
|
+
const { addDialog, removeDialog } = useDialogContext();
|
|
52
|
+
const handleClose = () => {
|
|
53
|
+
setModalVisible(false);
|
|
54
|
+
onClose?.();
|
|
55
|
+
removeDialog();
|
|
56
|
+
};
|
|
57
|
+
const clickEvent = (e) => {
|
|
58
|
+
|
|
59
|
+
if( !e.target.closest('.dialog') && !e.target.closest('.notification') ){
|
|
60
|
+
onClose?.();
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
addDialog();
|
|
65
|
+
document.addEventListener('mousedown', clickEvent);
|
|
66
|
+
return () => {
|
|
67
|
+
removeDialog();
|
|
68
|
+
document.removeEventListener('mousedown', clickEvent);
|
|
69
|
+
}
|
|
70
|
+
}, [addDialog, removeDialog]);
|
|
71
|
+
return showModal ? (
|
|
72
|
+
<div
|
|
73
|
+
aria-modal={true}
|
|
74
|
+
aria-label={title}
|
|
75
|
+
className={`${className ? className : ""} dialog ${isModal ? "dialog-modal" : ""}`}
|
|
76
|
+
>
|
|
77
|
+
<div className="dialog-header">
|
|
78
|
+
{title && <h2>{title}</h2>}
|
|
79
|
+
{isClosable && (
|
|
80
|
+
<Button className={"btn btn-close"} onClick={handleClose}>
|
|
81
|
+
<FaWindowClose />
|
|
82
|
+
</Button>
|
|
83
|
+
)}
|
|
84
|
+
</div>
|
|
85
|
+
<div className="dialog-content">{children}</div>
|
|
86
|
+
</div>
|
|
87
|
+
) : (
|
|
88
|
+
<></>
|
|
89
|
+
);
|
|
90
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
|
|
2
|
+
.dialog-bg {
|
|
3
|
+
position: fixed;
|
|
4
|
+
top: 0;
|
|
5
|
+
left: 0;
|
|
6
|
+
width: 100%;
|
|
7
|
+
height: 100%;
|
|
8
|
+
z-index: 10000;
|
|
9
|
+
pointer-events: none;
|
|
10
|
+
background-color: rgba(59, 59, 59, 0.51);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
#content .dialog {
|
|
14
|
+
box-sizing: border-box;
|
|
15
|
+
filter: opacity(1);
|
|
16
|
+
pointer-events: all;
|
|
17
|
+
position: fixed;
|
|
18
|
+
overflow: visible;
|
|
19
|
+
z-index: 10000;
|
|
20
|
+
top: 50%;
|
|
21
|
+
left: 50%;
|
|
22
|
+
transform: translate(-50%, -50%);
|
|
23
|
+
background-color: #F8F8F8;
|
|
24
|
+
border: 1px solid #DDDDDD;
|
|
25
|
+
border-radius: 8px;
|
|
26
|
+
color: black;
|
|
27
|
+
width: 90%;
|
|
28
|
+
max-width: 1024px;
|
|
29
|
+
min-width: 320px;
|
|
30
|
+
box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.25);
|
|
31
|
+
text-align: left;
|
|
32
|
+
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.dialog .actions {
|
|
36
|
+
justify-content: flex-end;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.dark .dialog {
|
|
40
|
+
background-color: #191919;
|
|
41
|
+
color: #e3e3e3;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#content .dialog.dialog-relation {
|
|
45
|
+
max-width: 480px;
|
|
46
|
+
}
|
|
47
|
+
.dialog-header {
|
|
48
|
+
display: flex;
|
|
49
|
+
gap: 8px;
|
|
50
|
+
justify-content: flex-start;
|
|
51
|
+
border: none;
|
|
52
|
+
border-bottom: 1px solid #E2E2E2;
|
|
53
|
+
h2 {
|
|
54
|
+
display: flex;
|
|
55
|
+
flex: 1;
|
|
56
|
+
padding: 0px;
|
|
57
|
+
margin: 0 0 0 16px;
|
|
58
|
+
font-size: 100%;
|
|
59
|
+
line-height: 208%;
|
|
60
|
+
}
|
|
61
|
+
.btn {
|
|
62
|
+
display: flex;
|
|
63
|
+
justify-self: flex-end;
|
|
64
|
+
&.btn-close {
|
|
65
|
+
position: absolute;
|
|
66
|
+
right: 0px;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
#ui .dialog-content {
|
|
72
|
+
padding: 16px;
|
|
73
|
+
cursor: auto;
|
|
74
|
+
word-wrap: break-word;
|
|
75
|
+
max-height: 85vh;
|
|
76
|
+
overflow-y: auto;
|
|
77
|
+
> .field {
|
|
78
|
+
margin: 0;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
table {
|
|
82
|
+
width: 100%;
|
|
83
|
+
border: 1px solid #DDDDDD;
|
|
84
|
+
border-collapse: collapse;
|
|
85
|
+
td,th {
|
|
86
|
+
padding: 3px;
|
|
87
|
+
@media only screen and (min-width: 480px) {
|
|
88
|
+
padding: 5px;
|
|
89
|
+
}
|
|
90
|
+
&.mini {
|
|
91
|
+
cursor: pointer;
|
|
92
|
+
min-width: auto;
|
|
93
|
+
width: 40px;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
thead tr {
|
|
97
|
+
background-color: #F1F1F1;
|
|
98
|
+
font-weight: lighter;
|
|
99
|
+
}
|
|
100
|
+
tbody tr {
|
|
101
|
+
}
|
|
102
|
+
@media only screen and (max-width: 480px){
|
|
103
|
+
font-size: 80%;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
.dark table {
|
|
107
|
+
thead tr {
|
|
108
|
+
background-color: #191919;
|
|
109
|
+
color: #e3e3e3;
|
|
110
|
+
}
|
|
111
|
+
tbody tr {
|
|
112
|
+
background-color: #191919;
|
|
113
|
+
}
|
|
114
|
+
td,th {
|
|
115
|
+
border: 1px solid #3f3f3f;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// C:/Dev/hackersonline-engine/client/src/DisplayFlexNodeRenderer.jsx
|
|
2
|
+
import {useTranslation} from "react-i18next";
|
|
3
|
+
import {cssProps} from "data-primals-engine/core";
|
|
4
|
+
import FlexDataRenderer from "./FlexDataRenderer.jsx";
|
|
5
|
+
import React from "react";
|
|
6
|
+
import {getFieldPathValue} from "data-primals-engine/data";
|
|
7
|
+
import {getFieldDefinitionFromPath} from "./core/data.js";
|
|
8
|
+
|
|
9
|
+
const DisplayFlexNodeRenderer = ({ node, data, dataIndexRef, allModels, baseModelFields, model }) => {
|
|
10
|
+
const { t } = useTranslation();
|
|
11
|
+
|
|
12
|
+
const baseStyle = node.type === 'container' ? node.containerStyle : node.itemStyle;
|
|
13
|
+
// MODIFICATION: Correction pour appliquer le CSS custom sur les items aussi
|
|
14
|
+
const customCssStyle = (node.type === 'container' ? node.containerStyle?.customCss : node.itemStyle?.customCss) || '';
|
|
15
|
+
const customStyle = customCssStyle ? cssProps(customCssStyle) : {};
|
|
16
|
+
const nodeStyle = { ...baseStyle, border: 'none', ...customStyle };
|
|
17
|
+
|
|
18
|
+
if (node.type === 'container') {
|
|
19
|
+
return (
|
|
20
|
+
<div
|
|
21
|
+
className="flex-node preview-container"
|
|
22
|
+
style={nodeStyle}
|
|
23
|
+
>
|
|
24
|
+
{node.children.length === 0 && <div className="empty-container-placeholder">{t('flexBuilder.emptyContainer', 'Conteneur vide.')}</div>}
|
|
25
|
+
{node.children.map((child) => (
|
|
26
|
+
<DisplayFlexNodeRenderer
|
|
27
|
+
key={child.id || `flex-child-${Math.random()}`}
|
|
28
|
+
node={child}
|
|
29
|
+
data={data}
|
|
30
|
+
dataIndexRef={dataIndexRef}
|
|
31
|
+
allModels={allModels}
|
|
32
|
+
baseModelFields={baseModelFields}
|
|
33
|
+
model={model}
|
|
34
|
+
/>
|
|
35
|
+
))}
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
} else if (node.type === 'item') {
|
|
39
|
+
let contentToRender;
|
|
40
|
+
|
|
41
|
+
if (node.content.type === 'dataField' && node.content.mapping) {
|
|
42
|
+
const dataItem = data && data.length > 0 ? data[dataIndexRef.current % data.length] : null;
|
|
43
|
+
let fieldValue;
|
|
44
|
+
let valueStatus = 'ok';
|
|
45
|
+
|
|
46
|
+
if (dataItem) {
|
|
47
|
+
fieldValue = getFieldPathValue(dataItem, node.content.mapping.fieldPath);
|
|
48
|
+
if (fieldValue === undefined || fieldValue === null) {
|
|
49
|
+
valueStatus = 'valueNotFound';
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
valueStatus = 'noDataAvailable';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const fieldDefinition = getFieldDefinitionFromPath(
|
|
56
|
+
node.content.mapping.fieldPath,
|
|
57
|
+
model,
|
|
58
|
+
allModels
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
if (valueStatus === 'ok' && fieldDefinition) {
|
|
62
|
+
contentToRender = (
|
|
63
|
+
<FlexDataRenderer
|
|
64
|
+
value={fieldValue}
|
|
65
|
+
data={dataItem}
|
|
66
|
+
fieldDefinition={fieldDefinition}
|
|
67
|
+
/>
|
|
68
|
+
);
|
|
69
|
+
} else {
|
|
70
|
+
const placeholderLabel = fieldDefinition?.translatedLabel || node.content.mapping.displayName || node.content.mapping.fieldPath;
|
|
71
|
+
if (valueStatus === 'valueNotFound') {
|
|
72
|
+
contentToRender = null;
|
|
73
|
+
} else if (valueStatus === 'noDataAvailable') {
|
|
74
|
+
contentToRender = <span className="preview-item-content-placeholder">{t('flexDataRenderer.noDataFor', '{{label}}: (pas de données)', { label: placeholderLabel })}</span>;
|
|
75
|
+
} else {
|
|
76
|
+
contentToRender = <span className="preview-item-content-placeholder error">{t('flexDataRenderer.missingDefFor', '{{label}}: (erreur/déf. manquante)', { label: placeholderLabel })}</span>;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
dataIndexRef.current++;
|
|
80
|
+
} else if (node.content.type === 'nestedContainer' && node.content.nestedContainer) {
|
|
81
|
+
contentToRender = (
|
|
82
|
+
<DisplayFlexNodeRenderer
|
|
83
|
+
node={node.content.nestedContainer}
|
|
84
|
+
data={data}
|
|
85
|
+
dataIndexRef={dataIndexRef}
|
|
86
|
+
allModels={allModels}
|
|
87
|
+
baseModelFields={baseModelFields}
|
|
88
|
+
model={model}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
} else if (node.content.type === 'richtext' && node.content.html) {
|
|
92
|
+
// --- NOUVEAU : GESTION DU RICHTEXT ---
|
|
93
|
+
contentToRender = (
|
|
94
|
+
<div className="richtext-content" dangerouslySetInnerHTML={{ __html: node.content.html }} />
|
|
95
|
+
);
|
|
96
|
+
} else {
|
|
97
|
+
contentToRender = (
|
|
98
|
+
<span className="preview-item-content-placeholder">
|
|
99
|
+
{/* Placeholder pour les autres types ou les items non configurés */}
|
|
100
|
+
</span>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
return (
|
|
104
|
+
contentToRender && <div
|
|
105
|
+
className={`flex-node preview-item ${node.content.type === 'dataField' ? 'is-data-badge-item' : ''} ${node.content.type === 'richtext' ? 'is-richtext-item' : ''}`}
|
|
106
|
+
style={nodeStyle}
|
|
107
|
+
title={node.content.type === 'dataField' && node.content.mapping ? `${t('mappedTo')} ${node.content.mapping.displayName}` : ''}
|
|
108
|
+
>
|
|
109
|
+
{contentToRender}
|
|
110
|
+
</div>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
};
|
|
115
|
+
export { DisplayFlexNodeRenderer};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// C:/Dev/hackersonline-engine/client/src/DocumentationPageLayout.jsx
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Outlet } from 'react-router-dom';
|
|
4
|
+
import './DocumentationPageLayout.scss'; // Nous créerons ce fichier SCSS ensuite
|
|
5
|
+
// C:/Dev/hackersonline-engine/client/src/DocumentationMenu.jsx
|
|
6
|
+
import { NavLink } from 'react-router-dom';
|
|
7
|
+
import { useTranslation } from 'react-i18next';
|
|
8
|
+
import { FaChevronDown, FaChevronRight } from 'react-icons/fa';
|
|
9
|
+
|
|
10
|
+
const DocMenuGroup = ({ title, children, initialOpen = true }) => {
|
|
11
|
+
const [isOpen, setIsOpen] = React.useState(initialOpen);
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div className="doc-menu-group">
|
|
15
|
+
<button onClick={() => setIsOpen(!isOpen)} className="doc-menu-group-title">
|
|
16
|
+
{isOpen ? <FaChevronDown /> : <FaChevronRight />}
|
|
17
|
+
<span>{title}</span>
|
|
18
|
+
</button>
|
|
19
|
+
{isOpen && <ul className="doc-menu-sublist">{children}</ul>}
|
|
20
|
+
</div>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const DocumentationMenu = () => {
|
|
25
|
+
const { t } = useTranslation();
|
|
26
|
+
|
|
27
|
+
// Définissez ici votre structure de menu
|
|
28
|
+
// Vous pouvez la rendre plus dynamique si nécessaire, par exemple en la chargeant depuis un JSON
|
|
29
|
+
const menuItems = [
|
|
30
|
+
{
|
|
31
|
+
title: t('doc.menu.gettingStarted', 'Bien commencer'),
|
|
32
|
+
links: [
|
|
33
|
+
{ path: '/documentation/getting-started', label: t('doc.menu.welcome', 'Les bases') },
|
|
34
|
+
{ path: '/documentation/concepts/model-vs-data', label: t('doc.menu.modelVsData', 'Modèles vs. Données') },
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
title: t('doc.menu.data', 'Concevez votre application'),
|
|
39
|
+
links: [
|
|
40
|
+
{ path: '/documentation/modeling/creating-models', label: t('doc.menu.creatingModels', 'Créer un modèle') },
|
|
41
|
+
// Ajoutez d'autres liens ici: Types de champs, Relations
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
title: t('doc.menu.apiUsage', 'Utiliser l\'API'),
|
|
46
|
+
links: [
|
|
47
|
+
{ path: '/documentation/api/guide', label: t('doc.menu.apiGuide', 'Documentation de l\'API') },
|
|
48
|
+
// Pour lister dynamiquement les modèles pour API Reference :
|
|
49
|
+
// Vous pourriez récupérer la liste des modèles via useModelContext ici
|
|
50
|
+
// et générer des liens comme `/documentation/api/reference/${model.name}`
|
|
51
|
+
// Exemple (simplifié, à adapter) :
|
|
52
|
+
// ...models.map(model => ({ path: `/documentation/api/reference/${model.name}`, label: model.name }))
|
|
53
|
+
],
|
|
54
|
+
}
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<nav className="documentation-menu">
|
|
59
|
+
{menuItems.map((group, index) => (
|
|
60
|
+
<DocMenuGroup key={index} title={group.title} initialOpen={index === 0}> {/* Ouvre le premier groupe par défaut */}
|
|
61
|
+
{group.links.map((link, linkIndex) => (
|
|
62
|
+
<li key={linkIndex}>
|
|
63
|
+
<NavLink
|
|
64
|
+
to={link.path}
|
|
65
|
+
className={({ isActive }) => (isActive ? 'doc-menu-item active' : 'doc-menu-item')}
|
|
66
|
+
>
|
|
67
|
+
{link.label}
|
|
68
|
+
</NavLink>
|
|
69
|
+
</li>
|
|
70
|
+
))}
|
|
71
|
+
</DocMenuGroup>
|
|
72
|
+
))}
|
|
73
|
+
</nav>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const DocumentationPageLayout = ({ topBarMenu }) => {
|
|
78
|
+
return (
|
|
79
|
+
<>
|
|
80
|
+
{topBarMenu} {/* Affiche la barre de menu supérieure globale */}
|
|
81
|
+
<div className="documentation-layout">
|
|
82
|
+
<aside className="documentation-sidebar">
|
|
83
|
+
<DocumentationMenu />
|
|
84
|
+
</aside>
|
|
85
|
+
<main className="documentation-content">
|
|
86
|
+
<Outlet /> {/* Affiche le contenu de la page de documentation active */}
|
|
87
|
+
</main>
|
|
88
|
+
</div>
|
|
89
|
+
</>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export default DocumentationPageLayout;
|