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,70 @@
1
+ {
2
+ "name": "client",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build --mode development",
9
+ "build:ssr-client": "vite build --ssrManifest --outDir dist/client --mode development",
10
+ "build:ssr-server": "vite build --ssr src/entry-server.jsx --outDir dist/server --mode development",
11
+ "lint": "eslint .",
12
+ "preview": "vite preview"
13
+ },
14
+ "dependencies": {
15
+ "@babel/runtime": "^7.24.7",
16
+ "@codeium/react-code-editor": "^1.0.12",
17
+ "@codemirror/lang-javascript": "^6.2.3",
18
+ "@tiptap/extension-code": "^2.12.0",
19
+ "@tiptap/extension-code-block": "^2.12.0",
20
+ "@tiptap/extension-code-block-lowlight": "^2.12.0",
21
+ "@tiptap/extension-color": "^2.11.5",
22
+ "@tiptap/extension-hard-break": "^2.12.0",
23
+ "@tiptap/extension-image": "^2.12.0",
24
+ "@tiptap/extension-link": "^2.12.0",
25
+ "@tiptap/extension-list-item": "^2.11.5",
26
+ "@tiptap/extension-text-style": "^2.11.5",
27
+ "@tiptap/pm": "^2.11.5",
28
+ "@tiptap/react": "^2.11.5",
29
+ "@tiptap/starter-kit": "^2.11.5",
30
+ "@uiw/react-codemirror": "^4.23.10",
31
+ "chart.js": "^4.4.9",
32
+ "chartjs-adapter-date-fns": "^3.0.0",
33
+ "classnames": "^2.5.1",
34
+ "date-fns": "^4.1.0",
35
+ "express-rate-limit": "^7.5.1",
36
+ "lowlight": "^3.3.0",
37
+ "react-chartjs-2": "^5.3.0",
38
+ "react-code-blocks": "^0.1.6",
39
+ "react-cookie": "^8.0.0",
40
+ "react-dom": "^18.3.1",
41
+ "react-ga": "^3.3.1",
42
+ "react-icons": "^5.0.1",
43
+ "react-international-phone": "^4.5.0",
44
+ "react-router": "^7.2.0",
45
+ "react-router-dom": "^7.1.5",
46
+ "react-syntax-highlighter": "^15.6.1",
47
+ "react-tooltip": "^5.28.1",
48
+ "uniqid": "^5.4.0",
49
+ "yet-another-react-lightbox": "^3.22.0",
50
+ "data-primals-engine": "^1.0.11",
51
+ "react-query": "^3.39.3"
52
+ },
53
+ "peerDependencies": {
54
+ "react": ">=18.0.0"
55
+ },
56
+ "devDependencies": {
57
+ "@eslint/js": "^9.17.0",
58
+ "@types/react": "^18.3.18",
59
+ "@types/react-dom": "^18.3.5",
60
+ "@vitejs/plugin-react": "^4.3.4",
61
+ "eslint": "^9.17.0",
62
+ "eslint-plugin-react": "^7.37.2",
63
+ "eslint-plugin-react-hooks": "^5.0.0",
64
+ "eslint-plugin-react-refresh": "^0.4.16",
65
+ "globals": "^15.14.0",
66
+ "process": "^0.11.10",
67
+ "sass-embedded": "^1.83.4",
68
+ "vite": "^6.1.1"
69
+ }
70
+ }
@@ -0,0 +1,214 @@
1
+ {
2
+ "info": {
3
+ "_postman_id": "4cafd155-48e1-4b6d-84a5-15290eee57af",
4
+ "name": "PROD",
5
+ "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
6
+ "_exporter_id": "42951162"
7
+ },
8
+ "item": [
9
+ {
10
+ "name": "Create a model",
11
+ "request": {
12
+ "method": "POST",
13
+ "header": [],
14
+ "body": {
15
+ "mode": "raw",
16
+ "raw": "{\r\n \"name\": \"dashboard\",\r\n \"description\": \"Configuration d'un tableau de bord personnalisé par l'utilisateur.\",\r\n \"maxRequestData\": 50,\r\n \"fields\": [\r\n {\r\n \"name\": \"name\",\r\n \"type\": \"string\",\r\n \"required\": true,\r\n \"hint\": \"Nom affiché et personnalisable du tableau de bord.\",\r\n \"locked\": null\r\n },\r\n {\r\n \"name\": \"description\",\r\n \"type\": \"string\",\r\n \"hint\": \"Description facultative pour donner plus de contexte au tableau de bord.\",\r\n \"locked\": null\r\n },\r\n {\r\n \"name\": \"layout\",\r\n \"type\": \"code\",\r\n \"language\": \"json\",\r\n \"required\": true,\r\n \"default\": \"{ \\\"type\\\": \\\"columns\\\", \\\"columns\\\": [] }\",\r\n \"hint\": \"Structure JSON décrivant l'organisation des KPIs. Exemple : { \\\"type\\\": \\\"columns\\\", \\\"columns\\\": [ [\\\"kpi_id_1\\\"], [\\\"kpi_id_2\\\", \\\"kpi_id_3\\\"] ] }.\",\r\n \"locked\": null\r\n },\r\n {\r\n \"name\": \"settings\",\r\n \"type\": \"code\",\r\n \"language\": \"json\",\r\n \"default\": \"{ \\\"defaultTimeRange\\\": \\\"last_7_days\\\", \\\"refreshInterval\\\": null }\",\r\n \"hint\": \"Paramètres JSON pour le tableau de bord, comme la plage de temps par défaut ('defaultTimeRange') ou l'intervalle de rafraîchissement en secondes ('refreshInterval').\",\r\n \"locked\": null\r\n },\r\n {\r\n \"name\": \"isDefault\",\r\n \"type\": \"boolean\",\r\n \"default\": false,\r\n \"hint\": \"Si 'true', ce tableau de bord est affiché par défaut pour l'utilisateur.\",\r\n \"locked\": null\r\n }\r\n ]\r\n}",
17
+ "options": {
18
+ "raw": {
19
+ "language": "json"
20
+ }
21
+ }
22
+ },
23
+ "url": {
24
+ "raw": "https://data.primals.net/api/model?_user=demo",
25
+ "protocol": "https",
26
+ "host": [
27
+ "data",
28
+ "primals",
29
+ "net"
30
+ ],
31
+ "path": [
32
+ "api",
33
+ "model"
34
+ ],
35
+ "query": [
36
+ {
37
+ "key": "_user",
38
+ "value": "demo"
39
+ }
40
+ ]
41
+ }
42
+ },
43
+ "response": []
44
+ },
45
+ {
46
+ "name": "Import a list of models",
47
+ "request": {
48
+ "method": "POST",
49
+ "header": [],
50
+ "body": {
51
+ "mode": "raw",
52
+ "raw": "{ \"models\" : [ \"translation\" ]}",
53
+ "options": {
54
+ "raw": {
55
+ "language": "json"
56
+ }
57
+ }
58
+ },
59
+ "url": {
60
+ "raw": "https://data.primals.net/api/models/import?_user=demo",
61
+ "protocol": "https",
62
+ "host": [
63
+ "data",
64
+ "primals",
65
+ "net"
66
+ ],
67
+ "path": [
68
+ "api",
69
+ "models",
70
+ "import"
71
+ ],
72
+ "query": [
73
+ {
74
+ "key": "_user",
75
+ "value": "demo"
76
+ },
77
+ {
78
+ "key": "",
79
+ "value": null,
80
+ "disabled": true
81
+ }
82
+ ]
83
+ }
84
+ },
85
+ "response": []
86
+ },
87
+ {
88
+ "name": "Add data to a model",
89
+ "request": {
90
+ "method": "POST",
91
+ "header": [],
92
+ "body": {
93
+ "mode": "raw",
94
+ "raw": "{\r\n \"model\": \"dashboard\",\r\n \"data\": {\r\n \"name\": \"Dashboard 1\",\r\n \"layout\": {}\r\n }\r\n}",
95
+ "options": {
96
+ "raw": {
97
+ "language": "json"
98
+ }
99
+ }
100
+ },
101
+ "url": {
102
+ "raw": "https://data.primals.net/api/data?_user=demo",
103
+ "protocol": "https",
104
+ "host": [
105
+ "data",
106
+ "primals",
107
+ "net"
108
+ ],
109
+ "path": [
110
+ "api",
111
+ "data"
112
+ ],
113
+ "query": [
114
+ {
115
+ "key": "_user",
116
+ "value": "demo"
117
+ }
118
+ ]
119
+ }
120
+ },
121
+ "response": []
122
+ },
123
+ {
124
+ "name": "Raw filtering",
125
+ "request": {
126
+ "method": "POST",
127
+ "header": [],
128
+ "body": {
129
+ "mode": "raw",
130
+ "raw": "{\"filter\":{\r\n \"key\": \"createaccount.content\",\r\n \"lang\": {\"$find\":{\"$and\":[\r\n {\"$or\":[\r\n {\"$and\":[\r\n {\"$regexMatch\":{\"input\":\"$$this.name\",\"regex\":\"en\"}}\r\n ]},{\"$regexMatch\":{\"input\":\"$$this.code\",\"regex\":\"en\"}}\r\n ]}\r\n ]}} \r\n}}",
131
+ "options": {
132
+ "raw": {
133
+ "language": "json"
134
+ }
135
+ }
136
+ },
137
+ "url": {
138
+ "raw": "https://data.primals.net/api/data/search?model=translation&_user=demo&depth=1",
139
+ "protocol": "https",
140
+ "host": [
141
+ "data",
142
+ "primals",
143
+ "net"
144
+ ],
145
+ "path": [
146
+ "api",
147
+ "data",
148
+ "search"
149
+ ],
150
+ "query": [
151
+ {
152
+ "key": "model",
153
+ "value": "translation"
154
+ },
155
+ {
156
+ "key": "_user",
157
+ "value": "demo"
158
+ },
159
+ {
160
+ "key": "depth",
161
+ "value": "1"
162
+ }
163
+ ]
164
+ }
165
+ },
166
+ "response": []
167
+ },
168
+ {
169
+ "name": "Big data filtering",
170
+ "request": {
171
+ "method": "POST",
172
+ "header": [],
173
+ "body": {
174
+ "mode": "raw",
175
+ "raw": "{\r\n \"filter\": {\r\n \"$find\": [\"recipient\", \r\n {\"$find\": [\"roles\", {\"$eq\": [\"$$this.name\", \"reg\"]}]}\r\n ]\r\n }\r\n}",
176
+ "options": {
177
+ "raw": {
178
+ "language": "json"
179
+ }
180
+ }
181
+ },
182
+ "url": {
183
+ "raw": "https://data.primals.net/api/data/search?model=message&_user=demo&depth=6",
184
+ "protocol": "https",
185
+ "host": [
186
+ "data",
187
+ "primals",
188
+ "net"
189
+ ],
190
+ "path": [
191
+ "api",
192
+ "data",
193
+ "search"
194
+ ],
195
+ "query": [
196
+ {
197
+ "key": "model",
198
+ "value": "message"
199
+ },
200
+ {
201
+ "key": "_user",
202
+ "value": "demo"
203
+ },
204
+ {
205
+ "key": "depth",
206
+ "value": "6"
207
+ }
208
+ ]
209
+ }
210
+ },
211
+ "response": []
212
+ }
213
+ ]
214
+ }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="107.5" height="28" role="img" aria-label="GITHUB"><title>GITHUB</title><g shape-rendering="crispEdges"><rect width="32" height="28" fill="black"/><rect x="32" width="75.5" height="28" fill="black"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="100"><image x="9" y="7" width="14" height="14" href="data:image/svg+xml;base64,PHN2ZyBmaWxsPSJ3aGl0ZSIgcm9sZT0iaW1nIiB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHRpdGxlPkdpdEh1YjwvdGl0bGU+PHBhdGggZD0iTTEyIC4yOTdjLTYuNjMgMC0xMiA1LjM3My0xMiAxMiAwIDUuMzAzIDMuNDM4IDkuOCA4LjIwNSAxMS4zODUuNi4xMTMuODItLjI1OC44Mi0uNTc3IDAtLjI4NS0uMDEtMS4wNC0uMDE1LTIuMDQtMy4zMzguNzI0LTQuMDQyLTEuNjEtNC4wNDItMS42MUM0LjQyMiAxOC4wNyAzLjYzMyAxNy43IDMuNjMzIDE3LjdjLTEuMDg3LS43NDQuMDg0LS43MjkuMDg0LS43MjkgMS4yMDUuMDg0IDEuODM4IDEuMjM2IDEuODM4IDEuMjM2IDEuMDcgMS44MzUgMi44MDkgMS4zMDUgMy40OTUuOTk4LjEwOC0uNzc2LjQxNy0xLjMwNS43Ni0xLjYwNS0yLjY2NS0uMy01LjQ2Ni0xLjMzMi01LjQ2Ni01LjkzIDAtMS4zMS40NjUtMi4zOCAxLjIzNS0zLjIyLS4xMzUtLjMwMy0uNTQtMS41MjMuMTA1LTMuMTc2IDAgMCAxLjAwNS0uMzIyIDMuMyAxLjIzLjk2LS4yNjcgMS45OC0uMzk5IDMtLjQwNSAxLjAyLjAwNiAyLjA0LjEzOCAzIC40MDUgMi4yOC0xLjU1MiAzLjI4NS0xLjIzIDMuMjg1LTEuMjMuNjQ1IDEuNjUzLjI0IDIuODczLjEyIDMuMTc2Ljc2NS44NCAxLjIzIDEuOTEgMS4yMyAzLjIyIDAgNC42MS0yLjgwNSA1LjYyNS01LjQ3NSA1LjkyLjQyLjM2LjgxIDEuMDk2LjgxIDIuMjIgMCAxLjYwNi0uMDE1IDIuODk2LS4wMTUgMy4yODYgMCAuMzE1LjIxLjY5LjgyNS41N0MyMC41NjUgMjIuMDkyIDI0IDE3LjU5MiAyNCAxMi4yOTdjMC02LjYyNy01LjM3My0xMi0xMi0xMiIvPjwvc3ZnPg=="/><text transform="scale(.1)" x="697.5" y="175" textLength="515" fill="#fff" font-weight="bold">GITHUB</text></g></svg>
Binary file
Binary file
Binary file
File without changes
@@ -0,0 +1,9 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="-11.5 -10.23174 23 20.46348">
2
+ <title>React Logo</title>
3
+ <circle cx="0" cy="0" r="2.05" fill="#61dafb"/>
4
+ <g stroke="#61dafb" stroke-width="1" fill="none">
5
+ <ellipse rx="11" ry="4.2"/>
6
+ <ellipse rx="11" ry="4.2" transform="rotate(60)"/>
7
+ <ellipse rx="11" ry="4.2" transform="rotate(120)"/>
8
+ </g>
9
+ </svg>
@@ -0,0 +1,328 @@
1
+ import React, {useEffect, useMemo, useState} from 'react';
2
+ import {useModelContext} from './contexts/ModelContext';
3
+ import {useAuthContext} from "./contexts/AuthContext.jsx";
4
+ import Button from "./Button.jsx";
5
+ import {Trans, useTranslation} from "react-i18next";
6
+ import {getDefaultForType} from "data-primals-engine/data";
7
+ import { CodeBlock, tomorrowNightBright } from 'react-code-blocks';
8
+ import {getObjectHash, shuffle} from "data-primals-engine/core";
9
+ import {mainFieldsTypes} from "data-primals-engine/constants";
10
+ import {end} from "express-mongo-sanitize";
11
+ import {FaFile, FaPlay, FaRunning} from "react-icons/fa";
12
+ import {Tooltip} from "react-tooltip";
13
+ import {CodeField} from "./Field.jsx";
14
+ import {useMutation} from "react-query";
15
+ import {FaF} from "react-icons/fa6";
16
+ import {getHost} from "./constants.js";
17
+
18
+ const Test= ({endpoint, lang, index, headers= {}}) => {
19
+ const qp = {...endpoint.queryParams};
20
+ const params = new URLSearchParams();
21
+ const isProd = import.meta.env.MODE === 'production';
22
+ Object.keys(qp || {}).forEach(k =>{
23
+ params.append(k, qp[k]);
24
+ })
25
+ params.append("lang", lang);
26
+ const [code, setCode] = useState(JSON.stringify(endpoint.body, null, 2), [endpoint]);
27
+
28
+
29
+ useEffect(() => {
30
+ setCode(JSON.stringify(endpoint.body, null, 2), [endpoint]);
31
+ }, [endpoint])
32
+
33
+ const [response,setResponse] = useState('');
34
+ const query = useMutation(() => {
35
+ return fetch((isProd ? 'https://'+getHost()+"/" : '')+'/api'+endpoint.endpoint+'?'+params.toString(), { method: endpoint.method, headers, body: code || JSON.stringify(endpoint.body) })
36
+ .then(e => e.json())
37
+ },{ onSuccess: (data) => {
38
+ setResponse(data)
39
+ }})
40
+
41
+ return (
42
+ <div key={index} className="api-endpoint">
43
+ <Tooltip id={"tooltip"} render={({content, activeAnchor}) => {
44
+
45
+ }}/>
46
+ <h2 className="description" dangerouslySetInnerHTML={{ __html: endpoint.title || endpoint.description }}></h2>
47
+ <button onClick={(e) => {
48
+ query.mutate();
49
+ const endpoint = e.target.closest('[data-endpoint]')?.dataset['endpoint'];
50
+ gtag('event', "select_content", {
51
+ content_type: "endpoint",
52
+ content_id: endpoint
53
+ });
54
+ }} data-tooltip-id="tooltip" data-endpoint={endpoint.method + " " + endpoint.endpoint} data-tooltip-html={'Run ' + endpoint.method+ ' /api'+endpoint.endpoint }><FaPlay /></button>
55
+ <span className="method">{endpoint.method}</span>
56
+ <span className="endpoint">{endpoint.endpoint}</span>
57
+ {response && <div className={"response fs-mini"}>
58
+ <CodeBlock language="json" theme={tomorrowNightBright}
59
+ showLineNumbers={false} text={JSON.stringify(response,null,4)} /></div>}
60
+ {Object.keys(qp || {}).length > 0 && (<h4>Query params</h4>)}
61
+ <ul>{Object.keys(qp || {}).map(m => {
62
+ return <li
63
+ className="query">
64
+ {( endpoint.requiredParams && endpoint.requiredParams.includes(m)) && (<span className="required">*</span>)}
65
+ <strong>{m}</strong> = {qp[m]}</li>
66
+ })}</ul>
67
+ {(endpoint.title || endpoint.description) !== endpoint.description && (<p className="description" dangerouslySetInnerHTML={{ __html: endpoint.description}}></p>)}
68
+ {endpoint.body && (<h4>Body Payload</h4>)}
69
+ {endpoint.body &&
70
+ <CodeField
71
+ value={code}
72
+ onChange={(e) => {
73
+ setCode(e.value);
74
+ }}/>}
75
+ </div>
76
+ )
77
+ };
78
+
79
+ const APIInfo = ({onInit}) => {
80
+ const {selectedModel, setSelectedModel, models} = useModelContext();
81
+ const [apiEndpoints, setApiEndpoints] = useState([]);
82
+
83
+ const {me} = useAuthContext();
84
+ const { t, i18n } = useTranslation()
85
+ const lang = (i18n.resolvedLanguage || i18n.language).split(/[-_]/)?.[0];
86
+
87
+ const getMatchingFilter = (model, datas, expr) => {
88
+ if(!model){
89
+ return null;
90
+ }
91
+ const parts = model.fields.filter(f=>f.asMain || f.required || f.type === "relation" || mainFieldsTypes.includes(f.type));
92
+ let fObj = {}, fa = [];
93
+ parts.forEach(field => {
94
+ const f = field.name;
95
+ if( field.type === 'datetime' || field.type === 'date'){
96
+ if( expr ){
97
+ fa.push({
98
+ '$lt': ['$'+f, datas[f]]
99
+ })
100
+ }else{
101
+ fObj[f] = datas[f];
102
+ }
103
+ }
104
+ else if( field.type === 'relation') {
105
+ if( expr){
106
+ fa.push({ [field.name]: { $find: {}} });
107
+ }else {
108
+ fObj[f] = (datas[f] ? getMatchingFilter(models.find(f => f.name === field.relation), datas[f], expr) : {$find: {}});
109
+ }
110
+ }else if( ['string', 'string_t', 'enum', 'richtext', 'url', 'password','email', 'phone'].includes(field.type)) {
111
+ if( expr ){
112
+ fa.push({"$regexMatch": {input: "$" + f, regex: datas[f]}});
113
+ }else
114
+ fObj[f] = datas[f];
115
+ }
116
+ else{
117
+ if( expr )
118
+ fa.push({ "$eq": ["$"+f, datas[f]] });
119
+ else
120
+ fObj[f] = datas[f];
121
+ }
122
+ });
123
+ return fa.length? fa : fObj;
124
+ }
125
+
126
+ useEffect(() => {
127
+ onInit?.();
128
+ }, [lang]);
129
+ useEffect(() => {
130
+ if ((!selectedModel || Object.keys(selectedModel).length === 0)) {
131
+ const mockedModel = {
132
+ name: 'modelPresentation',
133
+ description: 'a presentation model used for demonstration usage.',
134
+ fields: [{ name: 'views', type: 'number', default: 100},
135
+ { name: 'html', type: 'richtext', required: true},
136
+ { name: 'lastPublishedAt', type: 'datetime', required: true},
137
+ { name: 'comments', type: 'relation', relation: 'message', multiple: true}]
138
+ };
139
+ const endpoints = generateApiEndpoints(mockedModel);
140
+ setSelectedModel(mockedModel);
141
+ setApiEndpoints(endpoints);
142
+ return;
143
+ }
144
+
145
+ // Generate API endpoints based on the selected model
146
+ const endpoints = generateApiEndpoints(selectedModel);
147
+ setApiEndpoints(endpoints);
148
+ }, [selectedModel, lang, me]);
149
+
150
+ var mongoObjectId = function () {
151
+ var timestamp = (new Date().getTime() / 1000 | 0).toString(16);
152
+ return 'xxxxxxxxxxxxxxxx'.replace(/[x]/g, function() {
153
+ return (Math.random() * 16 | 0).toString(16);
154
+ }).toLowerCase();
155
+ };
156
+
157
+ const generateApiEndpoints = (model) => {
158
+ if (!model) return [];
159
+
160
+ let dt = {};
161
+ let orFields = { or : []};
162
+ model.fields.forEach(field => {
163
+ let val = getDefaultForType(field);
164
+ if( field.type === 'datetime' || field.type === 'date'){
165
+ val = new Date().toISOString();
166
+ }
167
+ else if( field.type === 'enum')
168
+ val = field.items[0];
169
+ else if( field.type === 'phone')
170
+ val = '+33606060606';
171
+ else if( field.type === 'email')
172
+ val = 'email@tld.com';
173
+ else if( Object.prototype.toString.call(val) === '[object Date]' ){
174
+ val = val.toISOString();
175
+ }
176
+ if(val!== undefined) {
177
+ orFields.or.push({[field.name]: val});
178
+ dt[field.name] = val;
179
+ }
180
+ });
181
+ let fields = getMatchingFilter(model, dt, false, true);
182
+
183
+ const modelName = model.name;
184
+ const searchFields = getMatchingFilter(model, dt, true, false);
185
+
186
+ let endpoints = []
187
+ if (!model.locked) {
188
+ endpoints.push(
189
+ {
190
+ method: 'POST',
191
+ endpoint: `/model`,
192
+ description: t('doc.api.createModel', { model: modelName }),
193
+ body: {
194
+ name: modelName,
195
+ description: model.description,
196
+ maxRequestData: 50,
197
+ fields: model.fields
198
+ }
199
+ })
200
+ endpoints.push({
201
+ method: 'PUT',
202
+ endpoint: `/model/:id`,
203
+ description: t('doc.api.updateModel', { model: modelName }),
204
+ body: {
205
+ model: {
206
+ name: modelName,
207
+ description: model.description,
208
+ fields: model.fields// Add some template for fields
209
+ }
210
+ }
211
+ })
212
+ endpoints.push({
213
+ method: 'DELETE',
214
+ endpoint: `/model`,
215
+ queryParams: {
216
+ name: modelName
217
+ },
218
+ requiredParams: ['model'],
219
+ description: t('doc.api.deleteModel', { model: modelName })
220
+ });
221
+ }
222
+ endpoints = endpoints.concat([
223
+ {
224
+ method: 'POST',
225
+ endpoint: `/data`,
226
+ description: t('doc.api.createData', { model: modelName}),
227
+ body: {
228
+ model: modelName,
229
+ data: fields // Add some template for data
230
+ }
231
+ },
232
+ {
233
+ title: t('doc.api.searchData.title', { model: modelName}),
234
+ method: 'POST',
235
+ endpoint: `/data/search`,
236
+ queryParams: {
237
+ model: modelName,
238
+ page: 1,
239
+ limit: 50,
240
+ expand: 1,
241
+ timeout: 5000,
242
+ sort:'createdAt:DESC,updatedAt:DESC'
243
+ },
244
+ requiredParams: ['model'],
245
+ description: t('doc.api.searchData', { model: modelName }),
246
+ body: { filter: { $or: searchFields } }
247
+ },
248
+ /*{
249
+ method: 'GET',
250
+ endpoint: `/api/data/${modelName}/:id`,
251
+ description: `Récupérer une entrée de ${modelName} par ID`,
252
+ },*/
253
+ {
254
+ method: 'PUT',
255
+ endpoint: `/data/:id`,
256
+ description: t('doc.api.updateData', { model: modelName }),
257
+ body: {
258
+ model: modelName,
259
+ data: fields // Add some template for data
260
+ }
261
+ },
262
+ {
263
+ method: 'DELETE',
264
+ endpoint: `/data/:id`,
265
+ description: t('doc.api.deleteData', { model: modelName }),
266
+ },
267
+ {
268
+ method: 'GET',
269
+ endpoint: `/models`,
270
+ description: t('doc.api.getModels', { model: modelName }),
271
+ },
272
+ ]);
273
+ return endpoints;
274
+ };
275
+
276
+ const [tokenVisible, setTokenVisible] = useState(false);
277
+
278
+ const objInit = {
279
+ 'Content-type': 'application/json',
280
+ '_user': me?.username || 'demo',
281
+ 'Authorization': 'Bearer ' + me?.token,
282
+ };
283
+ const obj = {...objInit,
284
+ 'Authorization': 'Bearer ' + (tokenVisible ? me?.token : (me?.token || '').substring(0, 10) + '...'),
285
+ };
286
+ if( /^demo[0-9]{1,2}$/.test(me?.username))
287
+ delete obj['Authorization'];
288
+ const isProd = import.meta.env.MODE === 'production';
289
+ const url = isProd ? 'https://'+getHost()+'/' : 'http://localhost:7632/';
290
+ return (
291
+ <div className="flex-1 api-info">
292
+ <h2><Trans i18nKey={"doc.api.title"}>API Documentation</Trans></h2>
293
+ <p className="hint">
294
+ API source endpoint: <span className={"endpoint"}>{url + 'api'}</span><br/><br/>
295
+ <a href={"/api-docs"} target="_blank"><FaFile/> Swagger / OpenAPI</a><br/>
296
+ <a href={"/doc/API.postman_collection.json"} target="_blank"><FaFile/> Collections Postman
297
+ (v2.1)</a>
298
+ </p>
299
+
300
+ <h3><Trans i18nKey={"doc.api.auth"}>Authentification</Trans></h3>
301
+ Headers :
302
+ <div className="fs-mini"><CodeBlock text={JSON.stringify(obj, null, 2)}
303
+ language="json" theme={tomorrowNightBright}
304
+ showLineNumbers={false}/></div>
305
+ {me && !/^demo[0-9]{1,2}$/.test(me.username) && (
306
+ <Button onClick={() => {
307
+ setTokenVisible(!tokenVisible)
308
+ }}>{tokenVisible ? <Trans i18nKey={"doc.api.hideToken"}>Cacher le token</Trans> :
309
+ <Trans i18nKey={"doc.api.seeToken"}>Voir le token</Trans>}</Button>
310
+ )}
311
+ {selectedModel ? (
312
+ <>
313
+ <div className="api-endpoints">
314
+ {apiEndpoints.map((endpoint, index) => <Test endpoint={endpoint} index={index} lang={lang} headers={objInit} />)}
315
+ </div>
316
+ </>
317
+ ) : (
318
+ <p>Sélectionnez un modèle pour voir la documentation.</p>
319
+ )}
320
+ <h2><Trans i18nKey={"doc.api.modelSource"}>Source du modèle</Trans></h2>
321
+ <div className="fs-mini"><CodeBlock text={JSON.stringify({...selectedModel, ...{locked: undefined, _id: undefined, _user: undefined}}, null, 2)} language="json"
322
+ theme={tomorrowNightBright} showLineNumbers={false}/></div>
323
+ </div>
324
+ )
325
+ ;
326
+ };
327
+
328
+ export default APIInfo;