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.
Files changed (43) hide show
  1. package/CHANGELOG.md +440 -0
  2. package/Dockerfile +19 -9
  3. package/DockerfileTest +19 -9
  4. package/README.md +58 -24
  5. package/client/package-lock.json +172 -1
  6. package/client/package.json +1 -0
  7. package/client/src/App.scss +4 -0
  8. package/client/src/CalendarConfigModal.jsx +60 -21
  9. package/client/src/CalendarView.jsx +107 -0
  10. package/client/src/CalendarView.scss +9 -0
  11. package/client/src/DataLayout.jsx +54 -27
  12. package/client/src/DataTable.jsx +19 -2
  13. package/client/src/Dialog.scss +4 -3
  14. package/client/src/Field.jsx +3 -2
  15. package/client/src/HistoryDialog.jsx +253 -0
  16. package/client/src/HistoryDialog.scss +320 -0
  17. package/client/src/ModelCreator.jsx +36 -15
  18. package/client/src/ModelCreatorField.jsx +5 -6
  19. package/client/src/ViewSwitcher.jsx +6 -6
  20. package/client/src/hooks/useTutorials.jsx +1 -1
  21. package/client/src/translations.js +298 -1
  22. package/package.json +2 -2
  23. package/src/email.js +1 -1
  24. package/src/engine.js +6 -6
  25. package/src/events.js +15 -5
  26. package/src/gameObject.js +0 -29
  27. package/src/modules/data/data.history.js +490 -0
  28. package/src/modules/data/data.js +139 -108
  29. package/src/modules/data/data.routes.js +4 -4
  30. package/src/modules/mongodb.js +17 -1
  31. package/src/modules/user.js +9 -3
  32. package/src/packs.js +2 -1
  33. package/src/services/stripe.js +67 -13
  34. package/src/workers/crypto-worker.js +3 -3
  35. package/test/data.backup.integration.test.js +5 -0
  36. package/test/data.history.integration.test.js +193 -0
  37. package/test/data.integration.test.js +79 -39
  38. package/test/events.test.js +27 -27
  39. package/test/globalTeardown.js +1 -0
  40. package/test/model.integration.test.js +5 -0
  41. package/test/workflow.actions.integration.test.js +2 -0
  42. package/test/workflow.integration.test.js +2 -0
  43. 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/Gemini
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 ≥ v18
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. | AIzaSyxxxxxxxxxxxxxxxxxxxx |
88
- | ANTHROPIC_API_KEY | Your optional Anthropic API key for AI features. | AIzaSyxxxxxxxxxxxxxxxxxxxx |
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
- ## 📁 Project Structure
541
- ```
542
- data-primals-engine/
543
- ├── src/
544
- │ ├── middlewares/
545
- │ ├── migrations/
546
- │ ├── modules/
547
- │ ├── workers/
548
- │ ├── engine.js // The Express engine that serves the API
549
- │ ├── constants.js // The inner-application constants definitions
550
- ├── packs.js // The packs that will be loaded and available with installPack() method
551
- ├── defaultModels.js // The default models available at startup.
552
- ├── ...
553
- └── server.js
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 override data | System | internal | (data) |
710
- | OnDataValidate | Triggered after a data internal validation check. | System | internal | (value, field, data) |
711
- | OnDataFilter | Triggered after a data internal data filtering operation. | System | internal | (filteredValue, field, data) |
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
@@ -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",
@@ -48,6 +48,7 @@
48
48
  "express-rate-limit": "^7.5.1",
49
49
  "lowlight": "^3.3.0",
50
50
  "react": ">=18.0.0",
51
+ "react-big-calendar": "^1.19.4",
51
52
  "react-dom": "^18.3.1",
52
53
  "react-router": "^7.2.0",
53
54
  "react-switch": "^7.1.0",
@@ -1294,6 +1294,10 @@ select[multiple]{
1294
1294
 
1295
1295
  .model-creator {
1296
1296
  .field-checkbox {
1297
+ padding: 8px;
1298
+ display: flex;
1299
+ gap: 8px;
1300
+ align-items: flex-start;
1297
1301
  .inline {
1298
1302
  flex-direction: row-reverse;
1299
1303
  }
@@ -1,5 +1,5 @@
1
1
  // src/components/dataview/CalendarConfigModal.jsx
2
- import React, { useState, useMemo } from '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 => f.type === 'date' || f.type === 'datetime'),
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
- if (selectedField) {
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.notConfigured')}</p>
33
-
34
- <div style={{ margin: '20px 0' }}>
35
- <label htmlFor="dateField">{t('dataview.calendar.dateFieldLabel')}</label>
36
- <br />
37
- <Select id="dateField" value={selectedField} onChange={e => setSelectedField(e.target.value)}>
38
- <option value="">{t('dataview.calendar.dateFieldPlaceholder')}</option>
39
- {dateFields.map(field => (
40
- <option key={field.name} value={field.name}>{field.name}</option>
41
- ))}
42
- </Select>
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={!selectedField}>{t('btns.saveConfig')}</Button>
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
  }