data-primals-engine 1.3.3 → 1.3.4

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.
@@ -30,7 +30,7 @@
30
30
  "express-rate-limit": "^7.5.1",
31
31
  "lowlight": "^3.3.0",
32
32
  "react": "18.3.1",
33
- "react-big-calendar": "^1.15.0",
33
+ "react-big-calendar": "1.15.0",
34
34
  "react-dom": "18.3.1",
35
35
  "react-router": "^7.2.0",
36
36
  "react-switch": "^6.1.0",
@@ -5619,9 +5619,10 @@
5619
5619
  }
5620
5620
  },
5621
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==",
5622
+ "version": "1.15.0",
5623
+ "resolved": "https://registry.npmjs.org/react-big-calendar/-/react-big-calendar-1.15.0.tgz",
5624
+ "integrity": "sha512-RNiPH1Vh/fpJpNIValpl6lHvuEroWkDvS8z3YW2QpmGUuAk6a0Q1uEujlQTd/gQrpKAaBA4Gyc1mzCdNIQ7DZQ==",
5625
+ "license": "MIT",
5625
5626
  "dependencies": {
5626
5627
  "@babel/runtime": "^7.20.7",
5627
5628
  "clsx": "^1.2.1",
@@ -5641,8 +5642,8 @@
5641
5642
  "uncontrollable": "^7.2.1"
5642
5643
  },
5643
5644
  "peerDependencies": {
5644
- "react": "^16.14.0 || ^17 || ^18 || ^19",
5645
- "react-dom": "^16.14.0 || ^17 || ^18 || ^19"
5645
+ "react": "^16.14.0 || ^17 || ^18",
5646
+ "react-dom": "^16.14.0 || ^17 || ^18"
5646
5647
  }
5647
5648
  },
5648
5649
  "node_modules/react-chartjs-2": {
@@ -48,7 +48,7 @@
48
48
  "express-rate-limit": "^7.5.1",
49
49
  "lowlight": "^3.3.0",
50
50
  "react": "18.3.1",
51
- "react-big-calendar": "^1.15.0",
51
+ "react-big-calendar": "1.15.0",
52
52
  "react-dom": "18.3.1",
53
53
  "react-router": "^7.2.0",
54
54
  "react-switch": "^6.1.0",
@@ -1505,3 +1505,38 @@ select[multiple]{
1505
1505
  }
1506
1506
 
1507
1507
 
1508
+ // Styles for TextField with suggestions
1509
+ .with-suggestions {
1510
+ position: relative;
1511
+ }
1512
+
1513
+ .suggestions-list {
1514
+ position: absolute;
1515
+ background-color: white;
1516
+ border: 1px solid #ccc;
1517
+ border-top: none;
1518
+ list-style: none;
1519
+ margin: 0;
1520
+ padding: 0;
1521
+ width: 100%;
1522
+ z-index: 1000; // High z-index to appear over other elements
1523
+ max-height: 250px;
1524
+ overflow-y: auto;
1525
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
1526
+ border-radius: 0 0 4px 4px;
1527
+
1528
+ li {
1529
+ padding: 8px 12px;
1530
+ cursor: pointer;
1531
+ display: flex;
1532
+ align-items: center;
1533
+ gap: 8px;
1534
+ font-size: 0.9rem;
1535
+
1536
+ &:hover {
1537
+ background-color: #f0f8ff; // A light blue hover
1538
+ }
1539
+ }
1540
+ }
1541
+
1542
+
@@ -32,6 +32,7 @@ import KanbanConfigModal from "./KanbanConfigModal.jsx";
32
32
  import CalendarConfigModal from "./CalendarConfigModal.jsx";
33
33
  import KanbanView from "./KanbanView.jsx";
34
34
  import CalendarView from "./CalendarView.jsx";
35
+ import {useParams, useSearchParams} from "react-router-dom";
35
36
 
36
37
 
37
38
  const NotConfiguredPlaceholder = ({ type, onConfigure }) => (
@@ -45,6 +46,7 @@ const NotConfiguredPlaceholder = ({ type, onConfigure }) => (
45
46
  );
46
47
 
47
48
  function DataLayout() {
49
+ const [ searchParams, setSearchParams ] = useSearchParams();
48
50
  const [viewSettings, setViewSettings] = useLocalStorage('viewSettings', {});
49
51
 
50
52
  const [isCalendarModalOpen, setCalendarModalOpen] = useState(false);
@@ -68,7 +70,8 @@ function DataLayout() {
68
70
  pagedFilters, pagedSort,
69
71
  elementsPerPage,
70
72
  setRelations,
71
- generatedModels
73
+ generatedModels,
74
+ models
72
75
  } = useModelContext(); // Utilisez le contexte
73
76
  const queryClient = useQueryClient();
74
77
 
@@ -87,6 +90,23 @@ function DataLayout() {
87
90
  const [showDataEditor, setDataEditorVisible] = useState(false);
88
91
  const [showAPIInfo, setAPIInfoVisible] = useState(false);
89
92
 
93
+ const mod = searchParams.get('model');
94
+ useEffect(() =>{
95
+ if (selectedModel?.name)
96
+ history.pushState({}, null, '?model='+selectedModel?.name);
97
+ }, [selectedModel?.name])
98
+ useEffect(() =>{
99
+ setSelectedModel(models.find(f => f.name === mod));
100
+ setEditionMode(false);
101
+ }, [mod, models])
102
+
103
+
104
+ useEffect(() => {
105
+ setSelectedModel(null)
106
+ setDataEditorVisible(false);
107
+ setAPIInfoVisible(false);
108
+ setEditionMode(true);
109
+ }, []);
90
110
  // La vue courante est dérivée du modèle sélectionné et des préférences stockées.
91
111
  const currentView = useMemo(() => {
92
112
  if (!selectedModel) return 'table';
@@ -426,13 +446,6 @@ function DataLayout() {
426
446
  queryClient.invalidateQueries(['api/data', model.name, r]);
427
447
  }
428
448
 
429
- useEffect(() => {
430
- setSelectedModel(null)
431
- setDataEditorVisible(false);
432
- setAPIInfoVisible(false);
433
- setEditionMode(true);
434
- }, []);
435
-
436
449
  const deleteMutation = useMutation((selectedModels) => {
437
450
  return fetch('/api/data/'+checkedItems.map(m => m._id).join(',')+'?lang='+lang+'&_user='+encodeURIComponent(getUserId(me)), {
438
451
  method: 'DELETE', headers: {
@@ -530,7 +543,7 @@ function DataLayout() {
530
543
 
531
544
 
532
545
  const [currentProfile, setCurrentProfile] = useLocalStorage('profile', null);
533
- const {isTourOpen, setIsTourOpen, currentTourSteps, allTourSteps, setTourStepIndex, setCurrentTourSteps, currentTour,setCurrentTour} = useUI();
546
+ const {isTourOpen, setIsTourOpen, currentTourSteps, allTourSteps, setTourStepIndex, setCurrentTourSteps, currentTour,setCurrentTour, addLaunchedTour} = useUI();
534
547
 
535
548
  const startTour = () => {
536
549
  setIsTourOpen(true);
@@ -538,12 +551,28 @@ function DataLayout() {
538
551
 
539
552
  const closeTour = (completedTourName) => {
540
553
  // On génère le nom du tour de démo pour le comparer
554
+ // This function is called when ANY tour is closed.
555
+ // It needs to persist the fact that the tour has been seen.
556
+
557
+ // 1. Add the tour's unique name to the list of launched tours.
558
+ // This list is managed by the UI context and stored in localStorage.
559
+ if (addLaunchedTour) { // Check if the function exists to be safe
560
+ addLaunchedTour(completedTourName);
561
+ }
562
+
563
+ // 2. Close the tour's UI.
564
+ setIsTourOpen(false);
565
+
566
+ // 3. Specific logic for the very first "demo" tour:
567
+ // We also set the user's profile to mark that they are no longer a brand new user.
568
+ // This prevents the demo tour from trying to launch on every page load.
541
569
  const demoTourName = `tour_${getObjectHash({steps: allTourSteps.demo || []})}`;
542
570
 
543
571
  // Si le tour qui vient de se terminer est le tour de démo
544
572
  if (completedTourName === demoTourName) {
545
573
 
546
574
  setIsTourOpen(false);
575
+ setCurrentProfile({ lastSeen: new Date().toISOString() });
547
576
  }
548
577
  };
549
578
 
@@ -41,9 +41,6 @@
41
41
  color: #e3e3e3;
42
42
  }
43
43
 
44
- #content .dialog.dialog-relation {
45
- max-width: 480px;
46
- }
47
44
  #content .dialog-header {
48
45
  display: flex;
49
46
  gap: 8px;
@@ -19,13 +19,14 @@ import {
19
19
  FaArrowDown,
20
20
  FaArrowUp, FaAt,
21
21
  FaCalendar, FaCalendarWeek, FaCode, FaFile,
22
- FaHashtag,
22
+ FaHashtag, FaIcons,
23
23
  FaImage,
24
24
  FaLink, FaListOl, FaListUl,
25
25
  FaLock, FaMinus,
26
26
  FaPallet, FaPhone, FaSitemap,
27
27
  FaToggleOn
28
28
  } from "react-icons/fa";
29
+ import * as Fa6Icons from 'react-icons/fa6'; // Importer Fa6
29
30
  import {FaCalendarDays, FaCodeCompare, FaPencil, FaT, FaTableColumns} from "react-icons/fa6";
30
31
  import { CodeBlock, tomorrowNightBright } from 'react-code-blocks';
31
32
  import SyntaxHighlighter from 'react-syntax-highlighter';
@@ -93,6 +94,7 @@ const TextField = forwardRef(function TextField(
93
94
  searchable,
94
95
  labelProps,
95
96
  showErrors=false,
97
+ before,
96
98
  after,
97
99
  ...rest
98
100
  },
@@ -187,6 +189,7 @@ const TextField = forwardRef(function TextField(
187
189
  ></textarea>
188
190
  )}
189
191
 
192
+ {before}
190
193
  <div className={"flex flex-1 flex-no-gap flex-start"}>
191
194
  {!mult && (
192
195
  <input
@@ -1491,7 +1494,77 @@ export const ModelField = ({field, disableable=false, showModel=true, value, fie
1491
1494
  )}
1492
1495
  </div>
1493
1496
  );
1494
- };export const ColorField = ({name, label, value, disabled, onChange, className, ...rest}) => {
1497
+ };
1498
+
1499
+ // Fonction pour obtenir le composant icône par son nom
1500
+ const getIconComponent = (iconName) => {
1501
+ if (!iconName) return null;
1502
+ const IconComponent = FaIcons[iconName] || Fa6Icons[iconName];
1503
+ return IconComponent ? <IconComponent /> : null; // Retourne l'élément React ou null
1504
+ };
1505
+ export const IconField = ({name, label, value, disabled, onChange, className, ...rest}) => {
1506
+ const { t } = useTranslation();
1507
+ const [iconSuggestions, setIconSuggestions] = useState([]);
1508
+ // Tri alphabétique pour une recherche plus prévisible
1509
+ const [allFaIcons] = useState(() => [...Object.keys(FaIcons), ...Object.keys(Fa6Icons)].sort());
1510
+
1511
+ const handleIconChange = (e) => {
1512
+ const value = e.target.value;
1513
+ onChange(value);
1514
+ if (value) {
1515
+ const filtered = allFaIcons.filter(
1516
+ icon => icon.toLowerCase().includes(value.toLowerCase())
1517
+ );
1518
+ setIconSuggestions(filtered.slice(0, 20));
1519
+ } else {
1520
+ setIconSuggestions([]);
1521
+ }
1522
+ };
1523
+
1524
+ const handleIconFocus = () => {
1525
+ if (value) {
1526
+ const filtered = allFaIcons.filter(
1527
+ icon => icon.toLowerCase().includes(value.toLowerCase())
1528
+ );
1529
+ setIconSuggestions(filtered.slice(0, 20));
1530
+ } else {
1531
+ setIconSuggestions(allFaIcons.slice(0, 10));
1532
+ }
1533
+ };
1534
+
1535
+ const onSuggestionClick = (suggestion) => {
1536
+ onChange(suggestion);
1537
+ setIconSuggestions([]);
1538
+ };
1539
+
1540
+ return <div className="textfield-wrapper with-suggestions">
1541
+ <div className={"flex flex-1 flex-no-wrap"}>
1542
+ <TextField
1543
+ help={t('modelcreator.field.icon')}
1544
+ id="modelIcon"
1545
+ disabled={disabled}
1546
+ value={value}
1547
+ label={label}
1548
+ before={<div>{getIconComponent(value)}</div>}
1549
+ onChange={handleIconChange}
1550
+ onFocus={handleIconFocus}
1551
+ onBlur={() => setTimeout(() => setIconSuggestions([]), 200)}
1552
+ autoComplete="off"
1553
+ />
1554
+ </div>
1555
+ {iconSuggestions.length > 0 && (
1556
+ <ul className="suggestions-list">
1557
+ {iconSuggestions.map(icon => (
1558
+ <li key={icon} onMouseDown={() => onSuggestionClick(icon)}>
1559
+ <span className="suggestion-icon">{getIconComponent(icon)}</span>
1560
+ <span>{icon}</span>
1561
+ </li>
1562
+ ))}
1563
+ </ul>
1564
+ )}
1565
+ </div>
1566
+ };
1567
+ export const ColorField = ({name, label, value, disabled, onChange, className, ...rest}) => {
1495
1568
  // 1. État interne pour une réactivité immédiate de l'interface.
1496
1569
  const [internalValue, setInternalValue] = useState(value);
1497
1570
 
@@ -5,7 +5,7 @@ import {useModelContext} from "./contexts/ModelContext.jsx";
5
5
  import {FaArrowDown, FaArrowUp, FaEdit, FaInfo, FaMinus, FaPlus, FaSave, FaTrash} from "react-icons/fa";
6
6
 
7
7
  import "./ModelCreator.scss"
8
- import {CheckboxField, CodeField, ColorField, NumberField, SelectField, TextField} from "./Field.jsx";
8
+ import {CheckboxField, CodeField, ColorField, IconField, NumberField, SelectField, TextField} from "./Field.jsx";
9
9
  import Button from "./Button.jsx";
10
10
  import {Trans, useTranslation} from "react-i18next";
11
11
  import {
@@ -42,6 +42,7 @@ const ModelCreator = forwardRef(({ initialPrompt = '', onModelGenerated, autoGen
42
42
  const [modelMaxRequestData, setModelMaxRequestData] = useState(initialModel?.maxRequestData || ''); // Utilisation de initialModel
43
43
  const [modelDescription, setModelDescription] = useState(initialModel?.name ? t(`model_description_${initialModel?.name}`, initialModel?.description) : '');
44
44
  const [modelHistory, setModelHistory] = useState(!!initialModel?.history || undefined);
45
+ const [modelIcon, setModelIcon] = useState(!!initialModel?.icon || undefined);
45
46
  const [fields, setFields] = useState([]);
46
47
  const [changed, setChanged] = useState(false);
47
48
 
@@ -55,6 +56,7 @@ const ModelCreator = forwardRef(({ initialPrompt = '', onModelGenerated, autoGen
55
56
  setModelDescription(initialModel.name ? t(`model_description_${initialModel.name}`, initialModel.description) : '');
56
57
  setFields([...(initialModel.fields || []).map(m => ({...m}))]);
57
58
  setModelHistory(initialModel.history);
59
+ setModelIcon(initialModel.icon);
58
60
  } else {
59
61
  // Mode création : on réinitialise tout pour une nouvelle génération
60
62
  setModelName('');
@@ -63,6 +65,7 @@ const ModelCreator = forwardRef(({ initialPrompt = '', onModelGenerated, autoGen
63
65
  setUseAI(true); // On active l'IA par défaut
64
66
  setModelVisible(false);
65
67
  setModelHistory(false);
68
+ setModelIcon(null);
66
69
  }
67
70
  }, [initialModel]);
68
71
 
@@ -74,6 +77,7 @@ const ModelCreator = forwardRef(({ initialPrompt = '', onModelGenerated, autoGen
74
77
  setModelDescription(selectedModel.description || '');
75
78
  setFields(selectedModel.fields || []);
76
79
  setModelMaxRequestData(selectedModel.maxRequestData || defaultMaxRequestData);
80
+ setModelIcon(selectedModel.icon || null);
77
81
  }
78
82
  }
79
83
  }, [generatedModels, selectedGeneratedModelIndex]);
@@ -134,6 +138,7 @@ const ModelCreator = forwardRef(({ initialPrompt = '', onModelGenerated, autoGen
134
138
  setModelMaxRequestData(defaultMaxRequestData);
135
139
  setFields([{ name: '', type: 'string' }]);
136
140
  setModelHistory(undefined);
141
+ setModelIcon(null);
137
142
  }
138
143
  setDatasToLoad(datas=>[...datas, modelName]);
139
144
 
@@ -177,6 +182,8 @@ const ModelCreator = forwardRef(({ initialPrompt = '', onModelGenerated, autoGen
177
182
  setModelName('');
178
183
  setModelDescription('');
179
184
  setSelectedModel(null)
185
+ setModelHistory(undefined)
186
+ setModelIcon(null);
180
187
  setFields([{ name: '', type: 'string', _isNewField: true }]);
181
188
  setDatasToLoad(datas => datas.filter(f => f !== modelName));
182
189
  setGeneratedModels(mods => {
@@ -213,6 +220,7 @@ const ModelCreator = forwardRef(({ initialPrompt = '', onModelGenerated, autoGen
213
220
  description: modelDescription,
214
221
  maxRequestData: modelMaxRequestData,
215
222
  history: modelHistory,
223
+ icon: modelIcon,
216
224
  fields: (fields || []).map((field) => {
217
225
  delete field['_isNewField'];
218
226
  let otherFields = [];
@@ -608,6 +616,20 @@ const ModelCreator = forwardRef(({ initialPrompt = '', onModelGenerated, autoGen
608
616
  }}
609
617
  />
610
618
 
619
+ <div className="flex field-bg">
620
+ <label htmlFor="modelIcon"><Trans i18nKey={"modelcreator.icon"}>Icône:</Trans></label>
621
+ </div>
622
+ <IconField
623
+ help={t('modelcreator.field.icon')}
624
+ id="modelIcon"
625
+ disabled={modelLocked}
626
+ value={modelIcon}
627
+ onChange={(e) => {
628
+ setModelIcon(e);
629
+ setChanged(true)
630
+ }}
631
+ />
632
+
611
633
  <div className="flex flex-no-wrap">
612
634
  <div className="checkbox-label flex flex-1">
613
635
  <CheckboxField
@@ -9,6 +9,17 @@ import useLocalStorage from "./hooks/useLocalStorage.js";
9
9
  import {Tooltip} from "react-tooltip";
10
10
  import {useUI} from "./contexts/UIContext.jsx";
11
11
  import {profiles} from "./constants.js";
12
+ import * as FaIcons from "react-icons/fa";
13
+ import * as Fa6Icons from "react-icons/fa6";
14
+
15
+
16
+ // Fonction pour obtenir le composant icône par son nom
17
+ const getIconComponent = (iconName) => {
18
+ if (!iconName) return null;
19
+ const IconComponent = FaIcons[iconName] || Fa6Icons[iconName];
20
+ return IconComponent ? <IconComponent /> : null; // Retourne l'élément React ou null
21
+ };
22
+
12
23
 
13
24
  export function ModelList({ onModelSelect, onCreateModel, onImportModel, onEditModel, onAPIInfo, onNewData }) {
14
25
  const {allTourSteps, setIsTourOpen,setCurrentTourSteps, setTourStepIndex, currentTour, setCurrentTour} = useUI();
@@ -34,6 +45,7 @@ export function ModelList({ onModelSelect, onCreateModel, onImportModel, onEditM
34
45
  return models.filter(model => {
35
46
  return (model.name && t('model_' + model.name, model.name).toLowerCase().includes(lowerSearchTerm)) ||
36
47
  (model.fields.some(f =>
48
+ (model.icon && model.icon.toLowerCase().includes(lowerSearchTerm)) ||
37
49
  f.name?.toLowerCase().includes(lowerSearchTerm) ||
38
50
  f.hint?.toLowerCase().includes(lowerSearchTerm)) ||
39
51
  (model.description && model.description.toLowerCase().includes(lowerSearchTerm)))
@@ -169,12 +181,18 @@ export function ModelList({ onModelSelect, onCreateModel, onImportModel, onEditM
169
181
  {filteredModels.sort((a, b) => t('model_' + a.name, a.name).localeCompare(t('model_' + b.name, b.name))).map((model) => {
170
182
  if (model._user !== me.username)
171
183
  return <></>
184
+
185
+
186
+ const IconComponent = getIconComponent(model.icon);
187
+
172
188
  return (
173
189
  <li data-testid={'model_'+model.name} className={`${model.name === selectedModel?.name ? 'active' : ''}`}
174
190
  key={'modelist' + model.name} onClick={() => generatedModels.some(g => g.name === model.name) ? onEditModel(model) : onModelSelect(model)}>
175
191
  <div className="flex flex-center flex-fw">
176
192
  <div
177
- className="flex-1 break-word">{t(`model_${model.name}`, model.name)} {generatedModels.some(f => f.name === model.name) ? '(tmp)' : (mods.some(f => f.mod === model.name) ? `(${mods.find(f => f.mod === model.name).count})` : '')}</div>
193
+ className="flex flex-1 break-word">
194
+ <div className={"icon"}>{IconComponent ? IconComponent : <></>}</div>
195
+ <div>{t(`model_${model.name}`, model.name)} {generatedModels.some(f => f.name === model.name) ? '(tmp)' : (mods.some(f => f.mod === model.name) ? `(${mods.find(f => f.mod === model.name).count})` : '')}</div></div>
178
196
  <div className="btns">
179
197
  {!generatedModels.some(g => g.name === model.name) && (<button data-tooltip-id="tooltipML" data-tooltip-content={t('btns.addData')} onClick={(e) => {
180
198
  e.stopPropagation();
@@ -1,5 +1,5 @@
1
1
  // ModelContext.jsx
2
- import React, {createContext, useContext, useEffect, useMemo, useState} from 'react';
2
+ import React, {createContext, useCallback, useContext, useEffect, useMemo, useState} from 'react';
3
3
  import useLocalStorage from "../hooks/useLocalStorage.js";
4
4
 
5
5
  const UIContext = createContext(null);
@@ -9,22 +9,31 @@ export const UIProvider = ({ children }) => {
9
9
  const [tourStepIndex, setTourStepIndex] = useState(0);
10
10
  const [currentTourSteps, setCurrentTourSteps] = useState([]);
11
11
  const [isTourOpen, setIsTourOpen] = useState(false);
12
- const [allTourSteps, setAllTourSteps] = useState([]);
13
- const [launchedTours, setLaunchedTours] = useState([]);
12
+ const [allTourSteps, setAllTourSteps] = useState({});
14
13
 
15
14
  const [currentTour, setCurrentTour] = useLocalStorage("spotlight-tour", null);
16
- const [finishedTours, setLSTours] = useLocalStorage('launchedTours', []);
17
- useEffect(() =>{
18
- if( launchedTours)
19
- setLSTours(launchedTours);
20
- }, [launchedTours])
15
+ // This is the single source of truth for tours that have been launched.
16
+ // It correctly reads from localStorage on initial load and persists any changes.
17
+ const [launchedTours, setLaunchedTours] = useLocalStorage('launchedTours', []);
18
+
19
+ // A stable helper function to add a tour to the list without overwriting.
20
+ const addLaunchedTour = useCallback((tourName) => {
21
+ setLaunchedTours(prevTours => {
22
+ // Ensure we're working with an array, even if localStorage is empty/corrupted.
23
+ const currentTours = Array.isArray(prevTours) ? prevTours : [];
24
+ if (!currentTours.includes(tourName)) {
25
+ return [...currentTours, tourName];
26
+ }
27
+ return currentTours; // Return unchanged if already present
28
+ });
29
+ }, [setLaunchedTours]); // setLaunchedTours from useLocalStorage is stable
21
30
 
22
31
  const [isLocked, setLocked] = useState(false);
23
32
  const contextValue = useMemo(() => ({
24
33
  isLocked,
25
34
  setLocked,
26
35
  currentTourSteps, setCurrentTourSteps,
27
- launchedTours,setLaunchedTours,
36
+ launchedTours, setLaunchedTours, addLaunchedTour,
28
37
  currentTour, setCurrentTour,
29
38
  isTourOpen, setIsTourOpen, setAllTourSteps, allTourSteps,
30
39
  tourStepIndex, setTourStepIndex
@@ -34,7 +43,7 @@ useEffect(() =>{
34
43
  launchedTours,setLaunchedTours,
35
44
  currentTour, setCurrentTour,
36
45
  isTourOpen, setIsTourOpen, setAllTourSteps, allTourSteps,
37
- tourStepIndex, setTourStepIndex]);
46
+ tourStepIndex, setTourStepIndex, addLaunchedTour]);
38
47
 
39
48
  return (
40
49
  <UIContext.Provider value={contextValue}>
@@ -2,6 +2,8 @@
2
2
  export const websiteTranslations = {
3
3
  fr: {
4
4
  translation: {
5
+ "modelcreator.icon": "Icône",
6
+ "modelcreator.field.icon": "Icône (FontAwesome) associée au modèle",
5
7
  "modelcreator.field.history": "Permet d'afficher et de superviser les modifications effectuées sur vos données. Consomme de l'espace.'",
6
8
  "history": "Historique",
7
9
  "history.op.create": "Création",
@@ -568,6 +570,8 @@ export const websiteTranslations = {
568
570
  },
569
571
  en: {
570
572
  translation: {
573
+ "modelcreator.icon": "Icon",
574
+ "modelcreator.field.icon": "Icon (FontAwesome) associated with the model",
571
575
  "modelcreator.field.history": "Allows you to view and monitor changes made to your data. Consumes space.",
572
576
  "history": "History",
573
577
  "history.op.create": "Creation",
@@ -2008,6 +2012,8 @@ export const websiteTranslations = {
2008
2012
  },
2009
2013
  es: {
2010
2014
  translation: {
2015
+ "modelcreator.icon": "Icono",
2016
+ "modelcreator.field.icon": "Icono (FontAwesome) asociado al modelo",
2011
2017
  "modelcreator.field.history": "Permite ver y supervisar los cambios realizados en los datos. Consume espacio.",
2012
2018
  "history": "Historial",
2013
2019
  "history.op.create": "Creación",
@@ -3450,6 +3456,8 @@ export const websiteTranslations = {
3450
3456
  },
3451
3457
  pt: {
3452
3458
  translation: {
3459
+ "modelcreator.icon": "Ícone",
3460
+ "modelcreator.field.icon": "Ícone (FontAwesome) associado ao modelo",
3453
3461
  "modelcreator.field.history": "Permite visualizar e monitorar as alterações feitas nos seus dados. Consome espaço.",
3454
3462
  "history": "Histórico",
3455
3463
  "history.op.create": "Criação",
@@ -4887,6 +4895,8 @@ export const websiteTranslations = {
4887
4895
  },
4888
4896
  de: {
4889
4897
  translation: {
4898
+ "modelcreator.icon": "Symbol",
4899
+ "modelcreator.field.icon": "Mit dem Modell verknüpftes Symbol (FontAwesome)",
4890
4900
  "modelcreator.field.history": "Ermöglicht Ihnen, Änderungen an Ihren Daten anzuzeigen und zu überwachen. Verbraucht Speicherplatz.",
4891
4901
  "history": "Verlauf",
4892
4902
 
@@ -6310,6 +6320,8 @@ export const websiteTranslations = {
6310
6320
  },
6311
6321
  it: {
6312
6322
  translation: {
6323
+ "modelcreator.icon": "Icona",
6324
+ "modelcreator.field.icon": "Icona (FontAwesome) associata al modello",
6313
6325
  "modelcreator.field.history": "Consente di visualizzare e monitorare le modifiche apportate ai dati. Occupa spazio.",
6314
6326
  "history": "Cronologia",
6315
6327
 
@@ -7745,6 +7757,8 @@ export const websiteTranslations = {
7745
7757
  },
7746
7758
  cs: {
7747
7759
  translation: {
7760
+ "modelcreator.icon": "Ikona",
7761
+ "modelcreator.field.icon": "Ikona (FontAwesome) přidružená k modelu",
7748
7762
  "modelcreator.field.history": "Umožňuje zobrazit a sledovat změny provedené ve vašich datech. Spotřebovává místo.",
7749
7763
  "history": "Historie",
7750
7764
  "history.op.create": "Vytvoření",
@@ -9174,6 +9188,8 @@ export const websiteTranslations = {
9174
9188
  },
9175
9189
  ru: {
9176
9190
  translation: {
9191
+ "modelcreator.icon": "Значок",
9192
+ "modelcreator.field.icon": "Значок (FontAwesome), связанный с моделью",
9177
9193
  "modelcreator.field.history": "Позволяет просматривать и отслеживать изменения, внесённые в ваши данные. Занимает место.",
9178
9194
  "history": "История",
9179
9195
 
@@ -10615,6 +10631,8 @@ export const websiteTranslations = {
10615
10631
  },
10616
10632
  ar: {
10617
10633
  translation: {
10634
+ "modelcreator.icon": "أيقونة",
10635
+ "modelcreator.field.icon": "الأيقونة (FontAwesome) المرتبطة بالنموذج",
10618
10636
  "modelcreator.field.history": "يسمح لك بعرض ومراقبة التغييرات التي أجريتها على بياناتك. يستهلك مساحة.",
10619
10637
  "history": "السجل",
10620
10638
  "history.op.create": "إنشاء",
@@ -12071,6 +12089,8 @@ export const websiteTranslations = {
12071
12089
  },
12072
12090
  sv: {
12073
12091
  translation: {
12092
+ "modelcreator.icon": "Ikon",
12093
+ "modelcreator.field.icon": "Ikon (FontAwesome) associerad med modellen",
12074
12094
  "modelcreator.field.history": "Låter dig visa och övervaka ändringar som gjorts i dina data. Tar plats.",
12075
12095
  "history": "Historik",
12076
12096
  "history.op.create": "Skapande",
@@ -13500,6 +13520,8 @@ export const websiteTranslations = {
13500
13520
  },
13501
13521
  el: {
13502
13522
  translation: {
13523
+ "modelcreator.icon": "Εικονίδιο",
13524
+ "modelcreator.field.icon": "Εικονίδιο (FontAwesome) που σχετίζεται με το μοντέλο",
13503
13525
  "modelcreator.field.history": "Σας επιτρέπει να βλέπετε και να παρακολουθείτε τις αλλαγές που έγιναν στα δεδομένα σας. Καταλαμβάνει χώρο.",
13504
13526
  "history": "Ιστορικό",
13505
13527
  "history.op.create": "Δημιουργία",
@@ -14938,6 +14960,8 @@ export const websiteTranslations = {
14938
14960
  },
14939
14961
  fa: {
14940
14962
  "translation": {
14963
+ "modelcreator.icon": "آیکون",
14964
+ "modelcreator.field.icon": "آیکون (FontAwesome) مرتبط با مدل",
14941
14965
  "modelcreator.field.history": "به شما امکان می‌دهد تغییرات ایجاد شده در داده‌های خود را مشاهده و نظارت کنید. فضا مصرف می‌کند.",
14942
14966
  "history": "تاریخچه",
14943
14967
  "history.op.create": "ایجاد",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "data-primals-engine",
3
- "version": "1.3.3",
3
+ "version": "1.3.4",
4
4
  "description": "data-primals-engine is a package responsible from handling large amount of data using MongoDB in a practical and performant way. It can also get workflow models working (for automation), and fully supports internationalisation. It also has integrated AI assistant.",
5
5
  "main": "src/engine.js",
6
6
  "type": "module",
@@ -92,6 +92,7 @@
92
92
  "react-markdown": "^10.1.0",
93
93
  "read-excel-file": "^5.8.8",
94
94
  "request-ip": "^3.3.0",
95
+ "safe-regex": "^2.1.1",
95
96
  "sanitize-html": "^2.17.0",
96
97
  "sirv": "^3.0.1",
97
98
  "stripe": "^18.4.0",
package/server.js CHANGED
@@ -11,6 +11,7 @@ import {port} from "./src/constants.js";
11
11
 
12
12
  Config.Set("modules", ["mongodb", "data", "file", "bucket", "workflow","user", "assistant", "swagger"])
13
13
  Config.Set("middlewares", []);
14
+ Config.Set("useDemoAccounts", true);
14
15
 
15
16
  const bench = GameObject.Create("Benchmark");
16
17
  const timer = bench.addComponent(BenchmarkTool);
package/src/core.js CHANGED
@@ -9,6 +9,15 @@ export function escapeRegex(string) {
9
9
  return string.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&');
10
10
  }
11
11
 
12
+ export function isValidRegex(s) {
13
+ try {
14
+ const m = s.match(/^([/~@;%#'])(.*?)\1([gimsuy]*)$/);
15
+ return m ? !!new RegExp(m[2],m[3])
16
+ : false;
17
+ } catch (e) {
18
+ return false
19
+ }
20
+ }
12
21
  export function escapeHtml(string){
13
22
  return string.replace(/(javascript|data|vbscript):/gi, '').replace(/[^\w-_. ]/gi, function (c) {
14
23
  return `&#${c.charCodeAt(0)};`;