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.
Files changed (166) hide show
  1. package/client/README.md +8 -0
  2. package/client/index.html +31 -0
  3. package/client/index.js +25 -0
  4. package/client/package-lock.json +11664 -0
  5. package/client/package.json +70 -0
  6. package/client/public/demo/26899917-d1ba-4df4-bb33-48d09a8778da.jpg +0 -0
  7. package/client/public/demo/4b9894c7-12cd-466d-8fd3-a2757b09ec96.jpg +0 -0
  8. package/client/public/demo/7ed369ac-a1a2-4b45-b0cd-4fd5bcf5a75a.jpg +0 -0
  9. package/client/public/doc/API.postman_collection.json +214 -0
  10. package/client/public/github.svg +1 -0
  11. package/client/public/profilCompany.jpg +0 -0
  12. package/client/public/profilDeveloper.jpg +0 -0
  13. package/client/public/profilEngineer.jpg +0 -0
  14. package/client/public/profilPersonal.jpg +0 -0
  15. package/client/public/profils .psd +0 -0
  16. package/client/public/react.svg +9 -0
  17. package/client/src/APIInfo.jsx +328 -0
  18. package/client/src/AddWidgetTypeModal.jsx +44 -0
  19. package/client/src/AddWidgetTypeModal.scss +87 -0
  20. package/client/src/App.css +42 -0
  21. package/client/src/App.jsx +586 -0
  22. package/client/src/App.scss +1466 -0
  23. package/client/src/AssistantChat.jsx +280 -0
  24. package/client/src/AssistantChat.scss +229 -0
  25. package/client/src/Button.jsx +12 -0
  26. package/client/src/Button.scss +197 -0
  27. package/client/src/CalculationBuilder.jsx +338 -0
  28. package/client/src/CalculationStepRow.jsx +199 -0
  29. package/client/src/CalendarConfigModal.jsx +49 -0
  30. package/client/src/ChartConfigModal.jsx +402 -0
  31. package/client/src/ConditionBuilder.jsx +622 -0
  32. package/client/src/ConditionBuilder.scss +672 -0
  33. package/client/src/ConditionBuilder2.jsx +757 -0
  34. package/client/src/ContentView.jsx +274 -0
  35. package/client/src/CronBuilder.jsx +126 -0
  36. package/client/src/CronBuilder.scss +128 -0
  37. package/client/src/CronPartBuilder.jsx +150 -0
  38. package/client/src/Dashboard.jsx +210 -0
  39. package/client/src/Dashboard.scss +445 -0
  40. package/client/src/DashboardChart.jsx +362 -0
  41. package/client/src/DashboardFlexViewItem.jsx +129 -0
  42. package/client/src/DashboardView.jsx +554 -0
  43. package/client/src/DataChart.jsx +349 -0
  44. package/client/src/DataEditor.jsx +556 -0
  45. package/client/src/DataLayout.jsx +709 -0
  46. package/client/src/DataTable.jsx +996 -0
  47. package/client/src/DataTable.scss +73 -0
  48. package/client/src/Dialog.jsx +90 -0
  49. package/client/src/Dialog.scss +117 -0
  50. package/client/src/DisplayFlexNodeRenderer.jsx +115 -0
  51. package/client/src/DocumentationPageLayout.jsx +93 -0
  52. package/client/src/DocumentationPageLayout.scss +262 -0
  53. package/client/src/Draggable.jsx +222 -0
  54. package/client/src/Draggable.scss +49 -0
  55. package/client/src/ExportDialog.jsx +172 -0
  56. package/client/src/Field.jsx +1558 -0
  57. package/client/src/FlexBuilder.jsx +269 -0
  58. package/client/src/FlexBuilder.scss +226 -0
  59. package/client/src/FlexBuilderControls.jsx +170 -0
  60. package/client/src/FlexBuilderModal.jsx +75 -0
  61. package/client/src/FlexBuilderModal.scss +109 -0
  62. package/client/src/FlexBuilderPreview.jsx +48 -0
  63. package/client/src/FlexBuilderUtils.js +84 -0
  64. package/client/src/FlexDataRenderer.jsx +170 -0
  65. package/client/src/FlexDataRenderer.scss +80 -0
  66. package/client/src/FlexNode.jsx +290 -0
  67. package/client/src/FlexTreeUtils.js +129 -0
  68. package/client/src/HiddenableCell.jsx +46 -0
  69. package/client/src/HiddenableCell.scss +36 -0
  70. package/client/src/KPIDialog.jsx +39 -0
  71. package/client/src/KPIWidget.jsx +139 -0
  72. package/client/src/KanbanCard.jsx +92 -0
  73. package/client/src/KanbanColumn.jsx +29 -0
  74. package/client/src/KanbanConfigModal.jsx +115 -0
  75. package/client/src/KanbanView.jsx +113 -0
  76. package/client/src/KanbanView.scss +107 -0
  77. package/client/src/MessageRotator.jsx +77 -0
  78. package/client/src/MessageRotator.scss +14 -0
  79. package/client/src/ModelCreator.jsx +640 -0
  80. package/client/src/ModelCreator.scss +154 -0
  81. package/client/src/ModelCreatorField.jsx +810 -0
  82. package/client/src/ModelImporter.jsx +120 -0
  83. package/client/src/ModelList.jsx +205 -0
  84. package/client/src/Notification.jsx +137 -0
  85. package/client/src/Notification.scss +126 -0
  86. package/client/src/NotificationProvider.jsx +73 -0
  87. package/client/src/PackGallery.jsx +211 -0
  88. package/client/src/PackGallery.scss +156 -0
  89. package/client/src/Pagination.jsx +141 -0
  90. package/client/src/RTE.jsx +415 -0
  91. package/client/src/RTETrans.jsx +103 -0
  92. package/client/src/RelationField.jsx +247 -0
  93. package/client/src/RelationValue.jsx +230 -0
  94. package/client/src/RestoreConfirmationModal.jsx +183 -0
  95. package/client/src/RestoreDialog.jsx +134 -0
  96. package/client/src/RestoreDialog.scss +67 -0
  97. package/client/src/RichTextEditorModal.jsx +34 -0
  98. package/client/src/RichTextEditorModal.scss +54 -0
  99. package/client/src/TourSpotlight.jsx +234 -0
  100. package/client/src/TourSpotlight.scss +100 -0
  101. package/client/src/TutorialsMenu.jsx +83 -0
  102. package/client/src/TutorialsMenu.scss +217 -0
  103. package/client/src/TutorialsRewardModal.jsx +65 -0
  104. package/client/src/Utils.jsx +78 -0
  105. package/client/src/ViewSwitcher.jsx +54 -0
  106. package/client/src/ViewSwitcher.scss +74 -0
  107. package/client/src/Webpage.jsx +69 -0
  108. package/client/src/_variables.scss +21 -0
  109. package/client/src/assets/react.svg +1 -0
  110. package/client/src/constants.js +68 -0
  111. package/client/src/contexts/AuthContext.jsx +28 -0
  112. package/client/src/contexts/ModelContext.jsx +328 -0
  113. package/client/src/contexts/UIContext.jsx +52 -0
  114. package/client/src/core/data.js +35 -0
  115. package/client/src/entry-client.jsx +15 -0
  116. package/client/src/entry-server.jsx +13 -0
  117. package/client/src/filter.js +222 -0
  118. package/client/src/hooks/data.js +101 -0
  119. package/client/src/hooks/useDebounce.js +20 -0
  120. package/client/src/hooks/useDragAndDrop.js +59 -0
  121. package/client/src/hooks/useLocalStorage.js +100 -0
  122. package/client/src/hooks/useMount.js +42 -0
  123. package/client/src/hooks/useTutorials.jsx +227 -0
  124. package/client/src/hooks/useWindowSize.js +24 -0
  125. package/client/src/i18n.js +47 -0
  126. package/client/src/index.css +90 -0
  127. package/client/src/index.js +10 -0
  128. package/client/src/main.jsx +11 -0
  129. package/client/src/translations.js +15940 -0
  130. package/client/src/tutorials.js +112 -0
  131. package/client/vite.config.js +30 -0
  132. package/eslint.config.js +50 -0
  133. package/package.json +48 -27
  134. package/server.js +9 -0
  135. package/src/constants.js +12 -23
  136. package/src/core.js +9 -8
  137. package/src/data.js +81 -80
  138. package/src/defaultModels.js +114 -115
  139. package/src/email.js +8 -9
  140. package/src/engine.js +5 -5
  141. package/src/events.js +1 -2
  142. package/src/i18n.js +5390 -211
  143. package/src/middlewares/middleware-mongodb.js +94 -93
  144. package/src/migrate.js +24 -24
  145. package/src/modules/assistant.js +55 -55
  146. package/src/modules/bucket.js +10 -9
  147. package/src/modules/data.js +387 -357
  148. package/src/modules/file.js +3 -6
  149. package/src/modules/mongodb.js +6 -7
  150. package/src/modules/user.js +24 -47
  151. package/src/modules/workflow.js +68 -67
  152. package/src/openai.jobs.js +2 -2
  153. package/src/packs.js +30 -31
  154. package/src/providers.js +101 -3
  155. package/src/setenv.js +1 -1
  156. package/src/user.js +0 -17
  157. package/src/workers/crypto-worker.js +21 -20
  158. package/src/workers/import-export-worker.js +18 -18
  159. package/test/data.backup.integration.test.js +7 -12
  160. package/test/data.integration.test.js +27 -25
  161. package/test/globalSetup.js +2 -2
  162. package/test/import_export.integration.test.js +6 -6
  163. package/test/model.integration.test.js +21 -22
  164. package/test/workflow.integration.test.js +2 -5
  165. package/test/workflow.robustness.test.js +6 -13
  166. 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;