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,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
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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
|
|
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;
|