data-primals-engine 1.3.0 → 1.3.2
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/CHANGELOG.md +440 -0
- package/Dockerfile +19 -9
- package/DockerfileTest +19 -9
- package/README.md +58 -24
- package/client/package-lock.json +172 -1
- package/client/package.json +1 -0
- package/client/src/App.scss +4 -0
- package/client/src/CalendarConfigModal.jsx +60 -21
- package/client/src/CalendarView.jsx +107 -0
- package/client/src/CalendarView.scss +9 -0
- package/client/src/DataLayout.jsx +54 -27
- package/client/src/DataTable.jsx +19 -2
- package/client/src/Dialog.scss +4 -3
- package/client/src/Field.jsx +3 -2
- package/client/src/HistoryDialog.jsx +253 -0
- package/client/src/HistoryDialog.scss +320 -0
- package/client/src/ModelCreator.jsx +36 -15
- package/client/src/ModelCreatorField.jsx +5 -6
- package/client/src/ViewSwitcher.jsx +6 -6
- package/client/src/hooks/useTutorials.jsx +1 -1
- package/client/src/translations.js +298 -1
- package/package.json +2 -2
- package/src/email.js +1 -1
- package/src/engine.js +6 -6
- package/src/events.js +15 -5
- package/src/gameObject.js +0 -29
- package/src/modules/data/data.history.js +490 -0
- package/src/modules/data/data.js +139 -108
- package/src/modules/data/data.routes.js +4 -4
- package/src/modules/mongodb.js +17 -1
- package/src/modules/user.js +9 -3
- package/src/packs.js +2 -1
- package/src/services/stripe.js +67 -13
- package/src/workers/crypto-worker.js +3 -3
- package/test/data.backup.integration.test.js +5 -0
- package/test/data.history.integration.test.js +193 -0
- package/test/data.integration.test.js +79 -39
- package/test/events.test.js +27 -27
- package/test/globalTeardown.js +1 -0
- package/test/model.integration.test.js +5 -0
- package/test/workflow.actions.integration.test.js +2 -0
- package/test/workflow.integration.test.js +2 -0
- package/test/workflow.robustness.test.js +2 -0
package/README.md
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
- **Automation Workflows**: Trigger complex actions based on data events (create, update, delete) or schedules (cron).
|
|
24
24
|
- **Advanced Querying & Aggregation**: Go beyond simple filters with deep relation expansion, complex lookups, and dynamic calculated fields.
|
|
25
25
|
- **Integrated Backup & Restore**: Secure, encrypted user data backups with rotation policies, supporting both local and AWS S3 storage.
|
|
26
|
+
- **Automatic Data Auditing**: Automatically tracks all changes (create, update, delete) for every record, providing a complete version history for auditing and traceability.
|
|
26
27
|
- **Event-Driven & Extensible**: A core event system allows for deep customization and the easy creation of new modules or plugins.
|
|
27
28
|
- **Authentication & Authorization**: Robust role-based access control (RBAC) and pluggable user providers.
|
|
28
29
|
- **Built-in File Management**: Handle file uploads seamlessly with integrated support for AWS S3 storage.
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
- **Modularity**: Enable/disable features as needed
|
|
40
41
|
- **Batteries Included**: Everything you need to get started quickly
|
|
41
42
|
- **Proven Performance**: Handles 50k+ documents efficiently
|
|
42
|
-
- **AI Ready**: Built-in LangChain integration for OpenAI
|
|
43
|
+
- **AI Ready**: Built-in LangChain integration for main providers (OpenAI,Gemini,Anthropic,DeepSeek)
|
|
43
44
|
|
|
44
45
|
---
|
|
45
46
|
|
|
@@ -56,7 +57,7 @@
|
|
|
56
57
|
### check
|
|
57
58
|
```bash
|
|
58
59
|
# Verify required versions
|
|
59
|
-
node -v # Must show ≥
|
|
60
|
+
node -v # Must show ≥ v20
|
|
60
61
|
mongod --version # Must be installed
|
|
61
62
|
```
|
|
62
63
|
|
|
@@ -84,8 +85,8 @@ MONGO_DB_URL=mongodb://127.0.0.1:27017
|
|
|
84
85
|
| JWT_SECRET | Secret key for signing JWT authentication tokens. | a_long_random_secret_string |
|
|
85
86
|
| OPENAI_API_KEY | Your optional OpenAI API key for AI features. | sk-xxxxxxxxxxxxxxxxxxxx |
|
|
86
87
|
| GOOGLE_API_KEY | Your optional Google (Gemini) API key for AI features. | AIzaSyxxxxxxxxxxxxxxxxxxxx |
|
|
87
|
-
| DEEPSEEK_API_KEY | Your optional DeepSeek API key for AI features. |
|
|
88
|
-
| ANTHROPIC_API_KEY | Your optional Anthropic API key for AI features. |
|
|
88
|
+
| DEEPSEEK_API_KEY | Your optional DeepSeek API key for AI features. | sk-xxxxxxxxxxxxxxxxxxxx |
|
|
89
|
+
| ANTHROPIC_API_KEY | Your optional Anthropic API key for AI features. | sk-ant-xxxxxxxxxxxxxxxxxxx |
|
|
89
90
|
| AWS_ACCESS_KEY_ID | AWS access key for S3 storage (files, backups). Keep empty to disable | AKIAIOSFODNN7EXAMPLE |
|
|
90
91
|
| AWS_SECRET_ACCESS_KEY | AWS secret access key. | wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY | |
|
|
91
92
|
| AWS_REGION | Region for your S3 bucket. | eu-west-3 | |
|
|
@@ -135,7 +136,7 @@ Define schemas using JSON:
|
|
|
135
136
|
}
|
|
136
137
|
```
|
|
137
138
|
### Smart Relations
|
|
138
|
-
- Handles up to 2,000 direct relations
|
|
139
|
+
- Handles up to 2,000 direct relations by default (can be customized)
|
|
139
140
|
- For larger datasets, use intermediate collections
|
|
140
141
|
- Automatic indexing on key fields
|
|
141
142
|
- Custom indexing on fields
|
|
@@ -211,6 +212,24 @@ This use case demonstrates how starter packs and workflows can automate complex,
|
|
|
211
212
|
|
|
212
213
|
---
|
|
213
214
|
|
|
215
|
+
## 📁 Project Structure
|
|
216
|
+
```
|
|
217
|
+
data-primals-engine/
|
|
218
|
+
├── src/
|
|
219
|
+
│ ├── middlewares/
|
|
220
|
+
│ ├── migrations/
|
|
221
|
+
│ ├── modules/
|
|
222
|
+
│ ├── workers/
|
|
223
|
+
│ ├── engine.js // The Express engine that serves the API
|
|
224
|
+
│ ├── constants.js // The inner-application constants definitions
|
|
225
|
+
│ ├── packs.js // The packs that will be loaded and available with installPack() method
|
|
226
|
+
│ ├── defaultModels.js // The default models available at startup.
|
|
227
|
+
│ ├── ...
|
|
228
|
+
└── server.js
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
214
233
|
## 🔌 API Examples (using `curl`)
|
|
215
234
|
|
|
216
235
|
### 📁 Model Management
|
|
@@ -536,23 +555,33 @@ const result = await installPack("61d1f1a9e3f1a9e3f1a9e3f1", user, "en");
|
|
|
536
555
|
> You can also open the pack gallery to see the JSON structure of each pack, before installing them.
|
|
537
556
|
|
|
538
557
|
---
|
|
558
|
+
### Data Auditing & History
|
|
559
|
+
data-primals-engine includes a built-in, automatic auditing system that creates a complete history for every record in your database.
|
|
539
560
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
561
|
+
>This feature is essential for traceability, debugging, and maintaining a clear audit trail of all data manipulations.
|
|
562
|
+
|
|
563
|
+
#### How It Works
|
|
564
|
+
The history feature is non-intrusive and fully automatic.
|
|
565
|
+
|
|
566
|
+
Whenever you use the standard data management functions (insertData, patchData, deleteData, etc.), the engine performs two actions:
|
|
567
|
+
1. It executes the requested operation (create, update, or delete) on the target document.
|
|
568
|
+
2. It saves a complete "snapshot" of the document's state into a dedicated history collection.
|
|
569
|
+
|
|
570
|
+
>Each history entry contains the original data along with crucial metadata:
|
|
571
|
+
- `_op`: The type of operation (i for insert, u for update, d for delete).
|
|
572
|
+
- `_v`: The version number of the record.
|
|
573
|
+
- `_user`: The username of the user who performed the action.
|
|
574
|
+
- `_updatedAt`: The timestamp of the operation.
|
|
575
|
+
- `_rid`: The ID of the original record, linking all history entries together.
|
|
576
|
+
|
|
577
|
+
### Accessing the History
|
|
578
|
+
You can retrieve the full history for any record via a simple API endpoint:
|
|
579
|
+
```x-sh
|
|
580
|
+
curl -X GET http://localhost:7633/api/data/history/:modelName/:recordId
|
|
554
581
|
```
|
|
555
582
|
|
|
583
|
+
---
|
|
584
|
+
|
|
556
585
|
## Workflows: Automate Your Business Logic
|
|
557
586
|
|
|
558
587
|
> Workflows are the automation engine of your application.
|
|
@@ -599,7 +628,8 @@ To create a custom endpoint, you need to define a document with the following st
|
|
|
599
628
|
"path": "postCount/:name",
|
|
600
629
|
"method": "GET",
|
|
601
630
|
"code": "const posts = await db.find('content', { author: { $find: { $eq: ['$lastName', request.params.name]}}}); return { postCount: posts.length };",
|
|
602
|
-
"isActive": true
|
|
631
|
+
"isActive": true,
|
|
632
|
+
"isPublic": true
|
|
603
633
|
}
|
|
604
634
|
```
|
|
605
635
|
| Field | Type | Description |
|
|
@@ -609,10 +639,12 @@ To create a custom endpoint, you need to define a document with the following st
|
|
|
609
639
|
| method | enum | The HTTP method: GET, POST, PUT, PATCH, or DELETE. |
|
|
610
640
|
| code | code | The JavaScript code to execute when the endpoint is called. |
|
|
611
641
|
| isActive | boolean | A flag to enable or disable the endpoint without deleting it. |
|
|
642
|
+
| isPublic | boolean | A flag to enable public access (private by default). |
|
|
612
643
|
---
|
|
613
644
|
|
|
614
645
|
### The Execution Context
|
|
615
646
|
Your JavaScript code runs in an async context and has access to several global objects that are securely injected into the sandbox:
|
|
647
|
+
Your current user is used to make the calls.
|
|
616
648
|
|
|
617
649
|
#### The context Object
|
|
618
650
|
> This object contains all the information about the incoming HTTP request.
|
|
@@ -706,15 +738,16 @@ Event.Listen("OnDataAdded", (engine, data) => {
|
|
|
706
738
|
| OnDataDeleted | Triggered just after data is actually deleted. | System & User | deleteData() | System: engine, {model, filter} (Pipeline*)<br>User: {model, filter} |
|
|
707
739
|
| OnDataSearched | Triggered after a data search. | System & User | searchData() | System: engine, {data, count} (Pipeline*)<br>User: {data, count} (or the version modified by the system) |
|
|
708
740
|
| OnDataExported | Triggered after a data export. | System & User | exportData() | System: engine, exportResults, modelsToExport (Pipeline*)<br>User: exportResults, modelsToExport (or the version modified by the system) |
|
|
709
|
-
| OnDataInsert | Triggered just before data insertion. It will use the
|
|
710
|
-
| OnDataValidate | Triggered
|
|
711
|
-
| OnDataFilter | Triggered
|
|
741
|
+
| OnDataInsert | Triggered just before data insertion. It will use the overrided data. | System | internal | (data) |
|
|
742
|
+
| OnDataValidate | Triggered to override validation check. | System | internal | (value, field, data) |
|
|
743
|
+
| OnDataFilter | Triggered to override data filtering operation. | System | internal | (filteredValue, field, data) |
|
|
744
|
+
| OnEmailTemplate | Triggered to override custom email templates | System | internal | (filteredValue, field, data) |
|
|
712
745
|
|
|
713
746
|
### Triggering events
|
|
714
747
|
|
|
715
748
|
If you want to provide your own hooks, you can call :
|
|
716
749
|
```javascript
|
|
717
|
-
const result = Event.Trigger("OnMyCustomEvent", "event", "user", ...args);
|
|
750
|
+
const result = await Event.Trigger("OnMyCustomEvent", "event", "user", ...args);
|
|
718
751
|
```
|
|
719
752
|
Results are merged together if multiple events are triggered.
|
|
720
753
|
- strings are concatenated
|
|
@@ -723,6 +756,7 @@ Results are merged together if multiple events are triggered.
|
|
|
723
756
|
- arrays are concatenated
|
|
724
757
|
- objects are merged using spread operator
|
|
725
758
|
|
|
759
|
+
|
|
726
760
|
---
|
|
727
761
|
|
|
728
762
|
## 🤝 Contributing
|
package/client/package-lock.json
CHANGED
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"express-rate-limit": "^7.5.1",
|
|
31
31
|
"lowlight": "^3.3.0",
|
|
32
32
|
"react": ">=18.0.0",
|
|
33
|
+
"react-big-calendar": "^1.19.4",
|
|
33
34
|
"react-dom": "^18.3.1",
|
|
34
35
|
"react-router": "^7.2.0",
|
|
35
36
|
"react-switch": "^7.1.0",
|
|
@@ -1370,7 +1371,6 @@
|
|
|
1370
1371
|
"version": "2.11.8",
|
|
1371
1372
|
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
|
1372
1373
|
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
|
1373
|
-
"peer": true,
|
|
1374
1374
|
"funding": {
|
|
1375
1375
|
"type": "opencollective",
|
|
1376
1376
|
"url": "https://opencollective.com/popperjs"
|
|
@@ -1381,6 +1381,17 @@
|
|
|
1381
1381
|
"resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-3.0.0.tgz",
|
|
1382
1382
|
"integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg=="
|
|
1383
1383
|
},
|
|
1384
|
+
"node_modules/@restart/hooks": {
|
|
1385
|
+
"version": "0.4.16",
|
|
1386
|
+
"resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz",
|
|
1387
|
+
"integrity": "sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==",
|
|
1388
|
+
"dependencies": {
|
|
1389
|
+
"dequal": "^2.0.3"
|
|
1390
|
+
},
|
|
1391
|
+
"peerDependencies": {
|
|
1392
|
+
"react": ">=16.8.0"
|
|
1393
|
+
}
|
|
1394
|
+
},
|
|
1384
1395
|
"node_modules/@rolldown/pluginutils": {
|
|
1385
1396
|
"version": "1.0.0-beta.27",
|
|
1386
1397
|
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
|
|
@@ -2199,6 +2210,11 @@
|
|
|
2199
2210
|
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
|
|
2200
2211
|
"peer": true
|
|
2201
2212
|
},
|
|
2213
|
+
"node_modules/@types/warning": {
|
|
2214
|
+
"version": "3.0.3",
|
|
2215
|
+
"resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz",
|
|
2216
|
+
"integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q=="
|
|
2217
|
+
},
|
|
2202
2218
|
"node_modules/@uiw/codemirror-extensions-basic-setup": {
|
|
2203
2219
|
"version": "4.24.2",
|
|
2204
2220
|
"resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.24.2.tgz",
|
|
@@ -2703,6 +2719,14 @@
|
|
|
2703
2719
|
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
|
|
2704
2720
|
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="
|
|
2705
2721
|
},
|
|
2722
|
+
"node_modules/clsx": {
|
|
2723
|
+
"version": "1.2.1",
|
|
2724
|
+
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
|
2725
|
+
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
|
|
2726
|
+
"engines": {
|
|
2727
|
+
"node": ">=6"
|
|
2728
|
+
}
|
|
2729
|
+
},
|
|
2706
2730
|
"node_modules/codemirror": {
|
|
2707
2731
|
"version": "6.0.2",
|
|
2708
2732
|
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz",
|
|
@@ -2877,6 +2901,11 @@
|
|
|
2877
2901
|
"url": "https://github.com/sponsors/ljharb"
|
|
2878
2902
|
}
|
|
2879
2903
|
},
|
|
2904
|
+
"node_modules/date-arithmetic": {
|
|
2905
|
+
"version": "4.1.0",
|
|
2906
|
+
"resolved": "https://registry.npmjs.org/date-arithmetic/-/date-arithmetic-4.1.0.tgz",
|
|
2907
|
+
"integrity": "sha512-QWxYLR5P/6GStZcdem+V1xoto6DMadYWpMXU82ES3/RfR3Wdwr3D0+be7mgOJ+Ov0G9D5Dmb9T17sNLQYj9XOg=="
|
|
2908
|
+
},
|
|
2880
2909
|
"node_modules/date-fns": {
|
|
2881
2910
|
"version": "4.1.0",
|
|
2882
2911
|
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
|
@@ -2886,6 +2915,11 @@
|
|
|
2886
2915
|
"url": "https://github.com/sponsors/kossnocorp"
|
|
2887
2916
|
}
|
|
2888
2917
|
},
|
|
2918
|
+
"node_modules/dayjs": {
|
|
2919
|
+
"version": "1.11.13",
|
|
2920
|
+
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
|
|
2921
|
+
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="
|
|
2922
|
+
},
|
|
2889
2923
|
"node_modules/debug": {
|
|
2890
2924
|
"version": "4.4.1",
|
|
2891
2925
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
|
@@ -2989,6 +3023,15 @@
|
|
|
2989
3023
|
"node": ">=0.10.0"
|
|
2990
3024
|
}
|
|
2991
3025
|
},
|
|
3026
|
+
"node_modules/dom-helpers": {
|
|
3027
|
+
"version": "5.2.1",
|
|
3028
|
+
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
|
|
3029
|
+
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
|
|
3030
|
+
"dependencies": {
|
|
3031
|
+
"@babel/runtime": "^7.8.7",
|
|
3032
|
+
"csstype": "^3.0.2"
|
|
3033
|
+
}
|
|
3034
|
+
},
|
|
2992
3035
|
"node_modules/dotenv": {
|
|
2993
3036
|
"version": "16.6.1",
|
|
2994
3037
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
|
|
@@ -3875,6 +3918,11 @@
|
|
|
3875
3918
|
"node": ">=10.13.0"
|
|
3876
3919
|
}
|
|
3877
3920
|
},
|
|
3921
|
+
"node_modules/globalize": {
|
|
3922
|
+
"version": "0.1.1",
|
|
3923
|
+
"resolved": "https://registry.npmjs.org/globalize/-/globalize-0.1.1.tgz",
|
|
3924
|
+
"integrity": "sha512-5e01v8eLGfuQSOvx2MsDMOWS0GFtCx1wPzQSmcHw4hkxFzrQDBO3Xwg/m8Hr/7qXMrHeOIE29qWVzyv06u1TZA=="
|
|
3925
|
+
},
|
|
3878
3926
|
"node_modules/globals": {
|
|
3879
3927
|
"version": "15.15.0",
|
|
3880
3928
|
"resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
|
|
@@ -4196,6 +4244,14 @@
|
|
|
4196
4244
|
"node": ">= 0.4"
|
|
4197
4245
|
}
|
|
4198
4246
|
},
|
|
4247
|
+
"node_modules/invariant": {
|
|
4248
|
+
"version": "2.2.4",
|
|
4249
|
+
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
|
|
4250
|
+
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
|
|
4251
|
+
"dependencies": {
|
|
4252
|
+
"loose-envify": "^1.0.0"
|
|
4253
|
+
}
|
|
4254
|
+
},
|
|
4199
4255
|
"node_modules/ipaddr.js": {
|
|
4200
4256
|
"version": "1.9.1",
|
|
4201
4257
|
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
|
@@ -4721,6 +4777,16 @@
|
|
|
4721
4777
|
"url": "https://github.com/sponsors/sindresorhus"
|
|
4722
4778
|
}
|
|
4723
4779
|
},
|
|
4780
|
+
"node_modules/lodash": {
|
|
4781
|
+
"version": "4.17.21",
|
|
4782
|
+
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
|
4783
|
+
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
|
4784
|
+
},
|
|
4785
|
+
"node_modules/lodash-es": {
|
|
4786
|
+
"version": "4.17.21",
|
|
4787
|
+
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
|
4788
|
+
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
|
4789
|
+
},
|
|
4724
4790
|
"node_modules/lodash.merge": {
|
|
4725
4791
|
"version": "4.6.2",
|
|
4726
4792
|
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
|
@@ -4761,6 +4827,14 @@
|
|
|
4761
4827
|
"yallist": "^3.0.2"
|
|
4762
4828
|
}
|
|
4763
4829
|
},
|
|
4830
|
+
"node_modules/luxon": {
|
|
4831
|
+
"version": "3.7.1",
|
|
4832
|
+
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.1.tgz",
|
|
4833
|
+
"integrity": "sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg==",
|
|
4834
|
+
"engines": {
|
|
4835
|
+
"node": ">=12"
|
|
4836
|
+
}
|
|
4837
|
+
},
|
|
4764
4838
|
"node_modules/markdown-it": {
|
|
4765
4839
|
"version": "14.1.0",
|
|
4766
4840
|
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
|
|
@@ -4809,6 +4883,11 @@
|
|
|
4809
4883
|
"node": ">= 0.8"
|
|
4810
4884
|
}
|
|
4811
4885
|
},
|
|
4886
|
+
"node_modules/memoize-one": {
|
|
4887
|
+
"version": "6.0.0",
|
|
4888
|
+
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
|
|
4889
|
+
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
|
|
4890
|
+
},
|
|
4812
4891
|
"node_modules/merge-descriptors": {
|
|
4813
4892
|
"version": "2.0.0",
|
|
4814
4893
|
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
|
|
@@ -4859,6 +4938,25 @@
|
|
|
4859
4938
|
"node": "*"
|
|
4860
4939
|
}
|
|
4861
4940
|
},
|
|
4941
|
+
"node_modules/moment": {
|
|
4942
|
+
"version": "2.30.1",
|
|
4943
|
+
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
|
4944
|
+
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
|
4945
|
+
"engines": {
|
|
4946
|
+
"node": "*"
|
|
4947
|
+
}
|
|
4948
|
+
},
|
|
4949
|
+
"node_modules/moment-timezone": {
|
|
4950
|
+
"version": "0.5.48",
|
|
4951
|
+
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz",
|
|
4952
|
+
"integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==",
|
|
4953
|
+
"dependencies": {
|
|
4954
|
+
"moment": "^2.29.4"
|
|
4955
|
+
},
|
|
4956
|
+
"engines": {
|
|
4957
|
+
"node": "*"
|
|
4958
|
+
}
|
|
4959
|
+
},
|
|
4862
4960
|
"node_modules/monaco-editor": {
|
|
4863
4961
|
"version": "0.45.0",
|
|
4864
4962
|
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.45.0.tgz",
|
|
@@ -5520,6 +5618,33 @@
|
|
|
5520
5618
|
"node": ">=0.10.0"
|
|
5521
5619
|
}
|
|
5522
5620
|
},
|
|
5621
|
+
"node_modules/react-big-calendar": {
|
|
5622
|
+
"version": "1.19.4",
|
|
5623
|
+
"resolved": "https://registry.npmjs.org/react-big-calendar/-/react-big-calendar-1.19.4.tgz",
|
|
5624
|
+
"integrity": "sha512-FrvbDx2LF6JAWFD96LU1jjloppC5OgIvMYUYIPzAw5Aq+ArYFPxAjLqXc4DyxfsQDN0TJTMuS/BIbcSB7Pg0YA==",
|
|
5625
|
+
"dependencies": {
|
|
5626
|
+
"@babel/runtime": "^7.20.7",
|
|
5627
|
+
"clsx": "^1.2.1",
|
|
5628
|
+
"date-arithmetic": "^4.1.0",
|
|
5629
|
+
"dayjs": "^1.11.7",
|
|
5630
|
+
"dom-helpers": "^5.2.1",
|
|
5631
|
+
"globalize": "^0.1.1",
|
|
5632
|
+
"invariant": "^2.2.4",
|
|
5633
|
+
"lodash": "^4.17.21",
|
|
5634
|
+
"lodash-es": "^4.17.21",
|
|
5635
|
+
"luxon": "^3.2.1",
|
|
5636
|
+
"memoize-one": "^6.0.0",
|
|
5637
|
+
"moment": "^2.29.4",
|
|
5638
|
+
"moment-timezone": "^0.5.40",
|
|
5639
|
+
"prop-types": "^15.8.1",
|
|
5640
|
+
"react-overlays": "^5.2.1",
|
|
5641
|
+
"uncontrollable": "^7.2.1"
|
|
5642
|
+
},
|
|
5643
|
+
"peerDependencies": {
|
|
5644
|
+
"react": "^16.14.0 || ^17 || ^18 || ^19",
|
|
5645
|
+
"react-dom": "^16.14.0 || ^17 || ^18 || ^19"
|
|
5646
|
+
}
|
|
5647
|
+
},
|
|
5523
5648
|
"node_modules/react-chartjs-2": {
|
|
5524
5649
|
"version": "5.3.0",
|
|
5525
5650
|
"resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.3.0.tgz",
|
|
@@ -5690,6 +5815,30 @@
|
|
|
5690
5815
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
|
5691
5816
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
|
5692
5817
|
},
|
|
5818
|
+
"node_modules/react-lifecycles-compat": {
|
|
5819
|
+
"version": "3.0.4",
|
|
5820
|
+
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
|
|
5821
|
+
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
|
|
5822
|
+
},
|
|
5823
|
+
"node_modules/react-overlays": {
|
|
5824
|
+
"version": "5.2.1",
|
|
5825
|
+
"resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-5.2.1.tgz",
|
|
5826
|
+
"integrity": "sha512-GLLSOLWr21CqtJn8geSwQfoJufdt3mfdsnIiQswouuQ2MMPns+ihZklxvsTDKD3cR2tF8ELbi5xUsvqVhR6WvA==",
|
|
5827
|
+
"dependencies": {
|
|
5828
|
+
"@babel/runtime": "^7.13.8",
|
|
5829
|
+
"@popperjs/core": "^2.11.6",
|
|
5830
|
+
"@restart/hooks": "^0.4.7",
|
|
5831
|
+
"@types/warning": "^3.0.0",
|
|
5832
|
+
"dom-helpers": "^5.2.0",
|
|
5833
|
+
"prop-types": "^15.7.2",
|
|
5834
|
+
"uncontrollable": "^7.2.1",
|
|
5835
|
+
"warning": "^4.0.3"
|
|
5836
|
+
},
|
|
5837
|
+
"peerDependencies": {
|
|
5838
|
+
"react": ">=16.3.0",
|
|
5839
|
+
"react-dom": ">=16.3.0"
|
|
5840
|
+
}
|
|
5841
|
+
},
|
|
5693
5842
|
"node_modules/react-query": {
|
|
5694
5843
|
"version": "3.39.3",
|
|
5695
5844
|
"resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz",
|
|
@@ -7101,6 +7250,20 @@
|
|
|
7101
7250
|
"url": "https://github.com/sponsors/ljharb"
|
|
7102
7251
|
}
|
|
7103
7252
|
},
|
|
7253
|
+
"node_modules/uncontrollable": {
|
|
7254
|
+
"version": "7.2.1",
|
|
7255
|
+
"resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz",
|
|
7256
|
+
"integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==",
|
|
7257
|
+
"dependencies": {
|
|
7258
|
+
"@babel/runtime": "^7.6.3",
|
|
7259
|
+
"@types/react": ">=16.9.11",
|
|
7260
|
+
"invariant": "^2.2.4",
|
|
7261
|
+
"react-lifecycles-compat": "^3.0.4"
|
|
7262
|
+
},
|
|
7263
|
+
"peerDependencies": {
|
|
7264
|
+
"react": ">=15.0.0"
|
|
7265
|
+
}
|
|
7266
|
+
},
|
|
7104
7267
|
"node_modules/uniqid": {
|
|
7105
7268
|
"version": "5.4.0",
|
|
7106
7269
|
"resolved": "https://registry.npmjs.org/uniqid/-/uniqid-5.4.0.tgz",
|
|
@@ -7285,6 +7448,14 @@
|
|
|
7285
7448
|
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
|
|
7286
7449
|
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="
|
|
7287
7450
|
},
|
|
7451
|
+
"node_modules/warning": {
|
|
7452
|
+
"version": "4.0.3",
|
|
7453
|
+
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
|
|
7454
|
+
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
|
|
7455
|
+
"dependencies": {
|
|
7456
|
+
"loose-envify": "^1.0.0"
|
|
7457
|
+
}
|
|
7458
|
+
},
|
|
7288
7459
|
"node_modules/which": {
|
|
7289
7460
|
"version": "2.0.2",
|
|
7290
7461
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
package/client/package.json
CHANGED
package/client/src/App.scss
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/components/dataview/CalendarConfigModal.jsx
|
|
2
|
-
import React, {
|
|
2
|
+
import React, {useState, useMemo, useEffect} from 'react';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import {Dialog} from "./Dialog.jsx";
|
|
5
5
|
|
|
@@ -8,42 +8,81 @@ const Modal = ({ isOpen, onClose, title, children }) => isOpen ? <div style={{..
|
|
|
8
8
|
const Select = ({ children, ...props }) => <select {...props}>{children}</select>;
|
|
9
9
|
const Button = (props) => <button {...props} />;
|
|
10
10
|
|
|
11
|
-
export default function CalendarConfigModal({ isOpen, onClose, onSave, modelFields }) {
|
|
11
|
+
export default function CalendarConfigModal({ isOpen, onClose, onSave, modelFields, initialSettings }) {
|
|
12
12
|
const { t } = useTranslation();
|
|
13
13
|
const [selectedField, setSelectedField] = useState('');
|
|
14
|
+
// État pour contenir la configuration complète
|
|
15
|
+
const [config, setConfig] = useState({
|
|
16
|
+
titleField: '',
|
|
17
|
+
startField: '',
|
|
18
|
+
endField: '',
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Remplir l'état à partir des `initialSettings` lorsque la modale s'ouvre
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (isOpen) {
|
|
24
|
+
setConfig({
|
|
25
|
+
titleField: initialSettings?.titleField || '',
|
|
26
|
+
startField: initialSettings?.startField || '',
|
|
27
|
+
endField: initialSettings?.endField || '',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}, [initialSettings, isOpen]);
|
|
31
|
+
|
|
32
|
+
const handleConfigChange = (e) => {
|
|
33
|
+
const { name, value } = e.target;
|
|
34
|
+
setConfig(prev => ({ ...prev, [name]: value }));
|
|
35
|
+
};
|
|
14
36
|
|
|
15
37
|
const dateFields = useMemo(() =>
|
|
16
|
-
modelFields.filter(f =>
|
|
38
|
+
modelFields.filter(f => ['date', 'datetime'].includes(f.type)),
|
|
17
39
|
[modelFields]
|
|
18
40
|
);
|
|
19
41
|
|
|
42
|
+
const titleFields = useMemo(() => modelFields.filter(f =>
|
|
43
|
+
['string', 'string_t', 'enum', 'number'].includes(f.type)
|
|
44
|
+
), [modelFields]);
|
|
45
|
+
|
|
46
|
+
|
|
20
47
|
const handleSave = () => {
|
|
21
|
-
|
|
22
|
-
onSave({ dateField: selectedField });
|
|
23
|
-
}
|
|
48
|
+
onSave(config);
|
|
24
49
|
};
|
|
25
50
|
|
|
51
|
+
const isSaveDisabled = !config.titleField || !config.startField || !config.endField;
|
|
52
|
+
|
|
26
53
|
if (!isOpen) return null;
|
|
27
54
|
|
|
28
55
|
return (
|
|
29
|
-
// Utilisez votre propre composant Modal ici
|
|
30
56
|
<Dialog isOpen={isOpen} onClose={onClose}>
|
|
31
|
-
<h2>{t('dataview.calendar.configureTitle')}</h2>
|
|
32
|
-
<p>{t('dataview.calendar.
|
|
33
|
-
|
|
34
|
-
<div style={{ margin: '20px 0' }}>
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
57
|
+
<h2>{t('dataview.calendar.configureTitle', 'Configurer la vue Calendrier')}</h2>
|
|
58
|
+
<p>{t('dataview.calendar.configureText', 'Sélectionnez les champs à utiliser pour afficher les données dans le calendrier.')}</p>
|
|
59
|
+
|
|
60
|
+
<div style={{ margin: '20px 0', display: 'flex', flexDirection: 'column', gap: '15px' }}>
|
|
61
|
+
<div>
|
|
62
|
+
<label htmlFor="titleField">{t('dataview.calendar.titleFieldLabel', "Champ pour le titre de l'événement")}</label>
|
|
63
|
+
<Select id="titleField" name="titleField" value={config.titleField} onChange={handleConfigChange}>
|
|
64
|
+
<option value="">{t('dataview.calendar.selectPlaceholder', 'Sélectionner un champ...')}</option>
|
|
65
|
+
{titleFields.map(field => (<option key={field.name} value={field.name}>{field.name}</option>))}
|
|
66
|
+
</Select>
|
|
67
|
+
</div>
|
|
68
|
+
<div>
|
|
69
|
+
<label htmlFor="startField">{t('dataview.calendar.startFieldLabel', 'Champ pour la date de début')}</label>
|
|
70
|
+
<Select id="startField" name="startField" value={config.startField} onChange={handleConfigChange}>
|
|
71
|
+
<option value="">{t('dataview.calendar.selectPlaceholder', 'Sélectionner un champ...')}</option>
|
|
72
|
+
{dateFields.map(field => (<option key={field.name} value={field.name}>{field.name}</option>))}
|
|
73
|
+
</Select>
|
|
74
|
+
</div>
|
|
75
|
+
<div>
|
|
76
|
+
<label htmlFor="endField">{t('dataview.calendar.endFieldLabel', 'Champ pour la date de fin')}</label>
|
|
77
|
+
<Select id="endField" name="endField" value={config.endField} onChange={handleConfigChange}>
|
|
78
|
+
<option value="">{t('dataview.calendar.selectPlaceholder', 'Sélectionner un champ...')}</option>
|
|
79
|
+
{dateFields.map(field => (<option key={field.name} value={field.name}>{field.name}</option>))}
|
|
80
|
+
</Select>
|
|
81
|
+
</div>
|
|
43
82
|
</div>
|
|
44
83
|
|
|
45
|
-
<Button onClick={handleSave} disabled={
|
|
46
|
-
<Button onClick={onClose} style={{ marginLeft: '10px' }}>{t('btns.cancel')}</Button>
|
|
84
|
+
<Button onClick={handleSave} disabled={isSaveDisabled}>{t('btns.saveConfig', 'Enregistrer')}</Button>
|
|
85
|
+
<Button onClick={onClose} style={{ marginLeft: '10px' }}>{t('btns.cancel', 'Annuler')}</Button>
|
|
47
86
|
</Dialog>
|
|
48
87
|
);
|
|
49
88
|
}
|