data-primals-engine 1.4.1 → 1.4.3

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 (49) hide show
  1. package/README.md +37 -22
  2. package/client/package-lock.json +33 -0
  3. package/client/package.json +1 -0
  4. package/client/src/App.scss +12 -4
  5. package/client/src/AssistantChat.jsx +48 -5
  6. package/client/src/AssistantChat.scss +0 -1
  7. package/client/src/ChartConfigModal.jsx +34 -9
  8. package/client/src/ConditionBuilder.jsx +1 -1
  9. package/client/src/ConditionBuilder2.jsx +2 -1
  10. package/client/src/Dashboard.jsx +396 -349
  11. package/client/src/DashboardChart.jsx +91 -12
  12. package/client/src/DataEditor.jsx +376 -368
  13. package/client/src/DataLayout.jsx +3 -2
  14. package/client/src/DataTable.jsx +60 -31
  15. package/client/src/Field.jsx +1788 -1782
  16. package/client/src/GeolocationField.jsx +94 -0
  17. package/client/src/ModelCreatorField.jsx +1 -0
  18. package/client/src/RelationField.jsx +2 -2
  19. package/client/src/constants.js +2 -2
  20. package/client/src/contexts/UIContext.jsx +5 -2
  21. package/client/src/filter.js +0 -155
  22. package/client/src/translations.js +16828 -16750
  23. package/package.json +10 -2
  24. package/src/config.js +2 -2
  25. package/src/constants.js +262 -4
  26. package/src/core.js +21 -3
  27. package/src/defaultModels.js +12 -12
  28. package/src/engine.js +7 -1
  29. package/src/events.js +4 -3
  30. package/src/filter.js +272 -260
  31. package/src/i18n.js +187 -177
  32. package/src/middlewares/middleware-mongodb.js +3 -1
  33. package/src/modules/assistant/assistant.js +131 -42
  34. package/src/modules/bucket.js +3 -2
  35. package/src/modules/data/data.backup.js +374 -0
  36. package/src/modules/data/data.core.js +2 -1
  37. package/src/modules/data/data.history.js +11 -8
  38. package/src/modules/data/data.js +190 -4551
  39. package/src/modules/data/data.operations.js +3000 -0
  40. package/src/modules/data/data.relations.js +687 -0
  41. package/src/modules/data/data.routes.js +132 -38
  42. package/src/modules/data/data.scheduling.js +274 -0
  43. package/src/modules/data/data.validation.js +248 -0
  44. package/src/modules/data/index.js +29 -1
  45. package/src/modules/user.js +2 -1
  46. package/src/modules/workflow.js +3 -2
  47. package/src/services/stripe.js +3 -2
  48. package/swagger-en.yml +133 -0
  49. package/test/model.integration.test.js +377 -221
@@ -0,0 +1,94 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { MapContainer, TileLayer, Marker, useMapEvents, Popup } from 'react-leaflet';
3
+ import 'leaflet/dist/leaflet.css';
4
+ import L from 'leaflet';
5
+
6
+ // Fix for default marker icon issue with webpack/parcel
7
+ delete L.Icon.Default.prototype._getIconUrl;
8
+
9
+ L.Icon.Default.mergeOptions({
10
+ iconRetinaUrl: 'https://unpkg.com/leaflet@1.7.1/dist/images/marker-icon-2x.png',
11
+ iconUrl: 'https://unpkg.com/leaflet@1.7.1/dist/images/marker-icon.png',
12
+ shadowUrl: 'https://unpkg.com/leaflet@1.7.1/dist/images/marker-shadow.png'
13
+ });
14
+
15
+
16
+ const LocationMarker = ({ position, setPosition, name, onChange, value }) => {
17
+ const map = useMapEvents({
18
+ click(e) {
19
+ const { lat, lng } = e.latlng;
20
+ const newPos = [lat, lng];
21
+ setPosition(newPos);
22
+ map.flyTo(newPos, map.getZoom());
23
+ onChange({
24
+ name: name,
25
+ value: {
26
+ type: 'Point',
27
+ coordinates: [lng, lat]
28
+ }
29
+ });
30
+ },
31
+ locationfound(e) {
32
+ const { lat, lng } = e.latlng;
33
+ const newPos = [lat, lng];
34
+ setPosition(newPos);
35
+ map.flyTo(newPos, map.getZoom());
36
+ onChange({
37
+ name: name,
38
+ value: {
39
+ type: 'Point',
40
+ coordinates: [lng, lat]
41
+ }
42
+ });
43
+ },
44
+ });
45
+
46
+ useEffect(() => {
47
+ // If there's no value, try to locate user
48
+ if (!value) {
49
+ map.locate();
50
+ }
51
+ }, [map, value]);
52
+
53
+
54
+ return position === null ? null : (
55
+ <Marker position={position}>
56
+ <Popup>Selected location</Popup>
57
+ </Marker>
58
+ );
59
+ }
60
+
61
+ const GeolocationField = ({ value, onChange, name }) => {
62
+ // Default to a central location if no value is provided, e.g., Paris
63
+ const initialPosition = (value && value.coordinates && value.coordinates.length === 2)
64
+ ? [value.coordinates[1], value.coordinates[0]] // Leaflet uses [lat, lng], GeoJSON uses [lng, lat]
65
+ : [48.8566, 2.3522]; // Default to Paris
66
+
67
+ const [position, setPosition] = useState(initialPosition);
68
+
69
+ useEffect(() => {
70
+ if (value && value.coordinates && value.coordinates.length === 2) {
71
+ const newPos = [value.coordinates[1], value.coordinates[0]];
72
+ if (position[0] !== newPos[0] || position[1] !== newPos[1]) {
73
+ setPosition(newPos);
74
+ }
75
+ } else {
76
+ // If value is cleared, reset to default. The map.locate() will try to find the user.
77
+ setPosition([48.8566, 2.3522]);
78
+ }
79
+ }, [value]);
80
+
81
+ return (
82
+ <div style={{ height: '300px', width: '100%' }}>
83
+ <MapContainer center={position} zoom={13} scrollWheelZoom={true} style={{ height: '100%', width: '100%' }}>
84
+ <TileLayer
85
+ attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
86
+ url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
87
+ />
88
+ <LocationMarker position={position} setPosition={setPosition} name={name} onChange={onChange} value={value} />
89
+ </MapContainer>
90
+ </div>
91
+ );
92
+ };
93
+
94
+ export default GeolocationField;
@@ -117,6 +117,7 @@ const ModelCreatorField = ({model, handleRenameField, handleRemoveField, handleU
117
117
  {label: t("field.array"), value: "array"},
118
118
  {label: t("field.relation"), value: "relation"},
119
119
  {label: t("field.file"), value: "file"},
120
+ {label: t("field.geolocation"), value: "geolocation"},
120
121
  {label: t("field.code"), value: "code"},
121
122
  {label: t("field.calculated", "calculated data"), value: "calculated"},
122
123
  {label: t("field.cronSchedule", "task schedule"), value: "cronSchedule"},
@@ -43,14 +43,14 @@ const RelationField = ({ field, help, onFocus, onBlur, onChange, value = null })
43
43
  // Recherche sur les champs principaux (asMain)
44
44
  model.fields.forEach(f => {
45
45
  if (f.asMain && mainFieldsTypes.includes(f.type)) {
46
- orConditions.push({ [f.name]: { "$regex": searchValue, "$options": "i" } });
46
+ orConditions.push({"$regexMatch": {input: "$" + f.name, regex: searchValue}});
47
47
  }
48
48
  });
49
49
  // Si aucun champ principal, recherche sur les champs texte
50
50
  if (orConditions.length === 0) {
51
51
  model.fields.forEach(f => {
52
52
  if (["string", "string_t", "richtext", "url"].includes(f.type)) {
53
- orConditions.push({ [f.name]: { "$regex": searchValue, "$options": "i" } });
53
+ orConditions.push({"$regexMatch": {input: "$" + f.name, regex: searchValue}});
54
54
  }
55
55
  });
56
56
  }
@@ -60,11 +60,11 @@ export const profiles = {
60
60
  "models": ['alert','endpoint','request','webpage', 'content', 'taxonomy', 'resource', 'translation', 'contact', 'location', 'channel', 'lang', 'token', 'message', 'ticket', 'user', 'permission', 'role']
61
61
  },
62
62
  'company': {
63
- "packs": ["E-commerce Starter Kit"],
63
+ "packs": ["Multilingual starter pack", "E-commerce Starter Kit"],
64
64
  "models": ['alert','request','location', 'order', 'currency', 'product', 'cart', 'cartItem', 'invoice', 'message', 'user', 'role', 'permission', 'token','translation', 'lang', 'webpage', 'content', 'taxonomy', 'contact', 'resource', 'accountingExercise', 'accountingLineItem', 'accountingEntry', 'employee', 'kpi', 'dashboard']
65
65
  },
66
66
  'engineer': {
67
- "packs": ["AI Content Generation - Starter Pack"],
67
+ "packs": ["Multilingual starter pack", "AI Content Generation - Starter Pack"],
68
68
  "models":['alert','endpoint','request','dashboard', 'kpi', 'user', 'role', 'token', 'permission', 'workflow', 'workflowRun', 'workflowStep', "channel", "message", 'workflowAction', 'workflowTrigger']
69
69
  }
70
70
  }
@@ -11,6 +11,8 @@ export const UIProvider = ({ children }) => {
11
11
  const [isTourOpen, setIsTourOpen] = useState(false);
12
12
  const [allTourSteps, setAllTourSteps] = useState({});
13
13
 
14
+ const [chartToAdd, setChartToAdd] = useState(null);
15
+
14
16
  const [currentTour, setCurrentTour] = useLocalStorage("spotlight-tour", null);
15
17
  // This is the single source of truth for tours that have been launched.
16
18
  // It correctly reads from localStorage on initial load and persists any changes.
@@ -36,14 +38,15 @@ export const UIProvider = ({ children }) => {
36
38
  launchedTours, setLaunchedTours, addLaunchedTour,
37
39
  currentTour, setCurrentTour,
38
40
  isTourOpen, setIsTourOpen, setAllTourSteps, allTourSteps,
39
- tourStepIndex, setTourStepIndex
41
+ tourStepIndex, setTourStepIndex, chartToAdd, setChartToAdd
40
42
  }), [isLocked,
41
43
  setLocked,
42
44
  currentTourSteps, setCurrentTourSteps,
43
45
  launchedTours,setLaunchedTours,
44
46
  currentTour, setCurrentTour,
45
47
  isTourOpen, setIsTourOpen, setAllTourSteps, allTourSteps,
46
- tourStepIndex, setTourStepIndex, addLaunchedTour]);
48
+ tourStepIndex, setTourStepIndex, addLaunchedTour,
49
+ chartToAdd, setChartToAdd]);
47
50
 
48
51
  return (
49
52
  <UIContext.Provider value={contextValue}>
@@ -1,159 +1,4 @@
1
1
 
2
- export const MONGO_CALC_OPERATORS = {
3
- $find: {
4
- label: 'Find in relation',
5
- description: 'Recherche dans une relation/tableau',
6
- supportsNesting: true,
7
- multi: false,
8
- isFindOperator: true // Nouvelle propriété pour identifier les opérateurs $find
9
- },
10
- '$regexMatch': {
11
- label: 'Find by regex',
12
- description: 'Find a string matching a regular expression (Ecmascript)',
13
- args: 2, // input et regex
14
- specialStructure: true // Indique une structure spéciale
15
- },
16
- $and: { label: 'Et (and)', description: 'Renvoie vrai si toutes les conditions sont vérifiées', args: 1, multi: true },
17
- $or: { label: 'Ou (or)', description: 'Renvoie vrai si au moins une des conditions est vérifiée.', args: 1, multi: true },
18
- $not: { label: 'Non (not)', description: 'Inverse la condition (ex: non égal à).', args: 1, multi: true },
19
- $nor: { label: 'Ou Non (nor)', description: 'Renvoie vrai si aucune des conditions n\'est vérifiée', args: 1, multi: true },
20
- $eq: { label: '=', multi: false, args: 2 },
21
- $ne: { label: '!=', multi: false, args: 2 },
22
- $gt: { label: '>', multi: false, args: 2 },
23
- $gte: { label: '>=', multi: false, args: 2 },
24
- $lt: { label: '<', multi: false, args: 2 },
25
- $lte: { label: '<=', multi: false, args: 2 },
26
- $in: { label: 'in', multi: true, args: 2 },
27
- $nin: { label: 'nin', multi: true, args: 2 },
28
- $all: { label: 'afll', multi: true },
29
- $size: {
30
- label: 'size',
31
- multi: false,
32
- description: 'Renvoie le nombre d\'éléments dans un tableau',
33
- disableAdvancedValue: true
34
- },
35
- $elemMatch: { label: 'elemMatch', multi: true },
36
- $type: {
37
- label: 'type',
38
- description: 'Renvoie le type BSON de l\'élément',
39
- multi: false,
40
- disableAdvancedValue: true
41
- },
42
- $add: { label: '+', multi: true },
43
- $subtract: { label: '-', multi: false, args: 2 },
44
- $multiply: { label: '*', multi: true },
45
- $divide: { label: '/', multi: false, args: 2 },
46
- $mod: { label: '%', multi: false, args: 2 },
47
- $pow: { label: 'pow', multi: false },
48
- $sqrt: { label: 'sqrt', multi: false },
49
- $exp: { label: 'exp', multi: false },
50
- $abs: { label: 'abs', multi: false },
51
- $ceil: { label: 'ceil', multi: false },
52
- $floor: { label: 'floor', multi: false },
53
- $ln: { label: 'ln', multi: false },
54
- $log10: { label: 'log', multi: false },
55
- $concat: { label: 'concat', multi: true },
56
-
57
- // Date-specific
58
- $year: { label: 'year', multi: false, isDate: true },
59
- $month: { label: 'month', multi: false, isDate: true },
60
- $dayOfMonth: { label: 'dayOfMonth', multi: false, isDate: true },
61
- $hour: { label: 'hour', multi: false, isDate: true },
62
- $minute: { label: 'minute', multi: false, isDate: true },
63
- $second: { label: 'second', multi: false, isDate: true },
64
- $millisecond: { label: 'ms', multi: false, isDate: true },
65
- // Date operators
66
- $dateAdd: {
67
- label: 'Add to date',
68
- description: 'Adds a duration to a date',
69
- isDate: true,
70
- specialStructure: true,
71
- args: [
72
- { name: 'startDate', label: 'Start Date', type: 'date' },
73
- { name: 'unit', label: 'Unit', type: 'select', options: ['year', 'month', 'day', 'hour', 'minute', 'second'] },
74
- { name: 'amount', label: 'Amount', type: 'number' },
75
- { name: 'timezone', label: 'Timezone', type: 'text', optional: true }
76
- ]
77
- },
78
- $dateSubtract: {
79
- label: 'Subtract from date',
80
- description: 'Subtracts a duration from a date',
81
- isDate: true,
82
- specialStructure: true,
83
- args: [
84
- { name: 'startDate', label: 'Start Date', type: 'date' },
85
- { name: 'unit', label: 'Unit', type: 'select', options: ['year', 'month', 'day', 'hour', 'minute', 'second'] },
86
- { name: 'amount', label: 'Amount', type: 'number' },
87
- { name: 'timezone', label: 'Timezone', type: 'text', optional: true }
88
- ]
89
- },
90
- $dateDiff: {
91
- label: 'Date Difference',
92
- description: 'Calculates the difference between two dates in specified units',
93
- isDate: true,
94
- specialStructure: true,
95
- args: [
96
- {
97
- name: 'startDate',
98
- label: 'Start Date',
99
- type: 'date',
100
- description: 'The starting date (inclusive)'
101
- },
102
- {
103
- name: 'endDate',
104
- label: 'End Date',
105
- type: 'date',
106
- description: 'The ending date (exclusive)'
107
- },
108
- {
109
- name: 'unit',
110
- label: 'Unit',
111
- type: 'select',
112
- options: ['year', 'month', 'week', 'day', 'hour', 'minute', 'second'],
113
- description: 'The unit for the result'
114
- },
115
- {
116
- name: 'timezone',
117
- label: 'Timezone',
118
- type: 'text',
119
- optional: true,
120
- description: 'The timezone (e.g. "Europe/Paris")'
121
- }
122
- ]
123
- },
124
- $dateToString: {
125
- label: 'Format date as string',
126
- description: 'Formats a date as a string',
127
- isDate: true,
128
- specialStructure: true,
129
- args: [
130
- {
131
- name: 'date',
132
- label: 'Date',
133
- type: 'date'
134
- },
135
- {
136
- name: 'format',
137
- label: 'Format',
138
- optional: true,
139
- type: 'text'
140
- },
141
- {
142
- name: 'timezone',
143
- label: 'Timezone',
144
- type: 'text',
145
- optional: true,
146
- description: 'The timezone (e.g. "Europe/Paris")'
147
- }
148
- ]
149
- },
150
-
151
- // Converters
152
- $toBool: { label: 'toBool', multi: false, converter: true },
153
- $toString: { label: 'toString', multi: false, converter: true },
154
- $toInt: { label: 'toInt', multi: false, converter: true },
155
- $toDouble: { label: 'toDouble', multi: false, converter: true }
156
- };
157
2
 
158
3
  export const convertInputValue = (value) => {
159
4
  if (typeof value !== 'string') return value;