data-primals-engine 1.1.8 → 1.2.1

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 (44) hide show
  1. package/README.md +72 -25
  2. package/client/src/App.scss +11 -6
  3. package/client/src/AssistantChat.scss +0 -1
  4. package/client/src/ChartConfigModal.jsx +0 -1
  5. package/client/src/ConditionBuilder.jsx +0 -2
  6. package/client/src/ConditionBuilder2.jsx +47 -6
  7. package/client/src/ContentView.jsx +0 -1
  8. package/client/src/CronBuilder.scss +0 -1
  9. package/client/src/CronPartBuilder.jsx +0 -1
  10. package/client/src/Dashboard.jsx +0 -1
  11. package/client/src/Dashboard.scss +0 -1
  12. package/client/src/DashboardChart.jsx +0 -1
  13. package/client/src/DashboardView.jsx +0 -1
  14. package/client/src/DataEditor.jsx +0 -4
  15. package/client/src/DisplayFlexNodeRenderer.jsx +0 -1
  16. package/client/src/DocumentationPageLayout.jsx +0 -2
  17. package/client/src/DocumentationPageLayout.scss +0 -1
  18. package/client/src/Field.jsx +1 -1
  19. package/client/src/FlexNode.jsx +0 -1
  20. package/client/src/KanbanView.scss +0 -1
  21. package/client/src/ModelCreator.jsx +4 -4
  22. package/client/src/ModelCreator.scss +10 -0
  23. package/client/src/ModelCreatorField.jsx +34 -34
  24. package/client/src/RTE.jsx +2 -1
  25. package/client/src/RestoreDialog.jsx +0 -1
  26. package/client/src/TourSpotlight.jsx +0 -1
  27. package/client/src/filter.js +2 -0
  28. package/client/src/hooks/useDragAndDrop.js +0 -1
  29. package/issues.txt +0 -0
  30. package/package.json +3 -2
  31. package/src/constants.js +6 -0
  32. package/src/core.js +6 -0
  33. package/src/data.js +0 -2
  34. package/src/defaultModels.js +33 -0
  35. package/src/migrate.js +0 -1
  36. package/src/modules/assistant.js +211 -133
  37. package/src/modules/data.js +88 -69
  38. package/src/modules/user.js +111 -56
  39. package/src/modules/workflow.js +10 -12
  40. package/src/providers.js +1 -1
  41. package/src/workers/crypto-worker.js +0 -1
  42. package/test/data.integration.test.js +53 -70
  43. package/test/user.test.js +203 -0
  44. package/test/workflow.integration.test.js +1 -1
package/README.md CHANGED
@@ -9,6 +9,11 @@
9
9
 
10
10
  > Whether you're building a CRM, e-commerce site, CMS, or SaaS platform, **data-primals-engine** provides the strong foundations so you can focus on what makes your application unique.
11
11
 
12
+ <p align="center">
13
+ <a href="https://data.primals.net/prez1.jpg" target="_blank"><img alt="Light" src="https://data.primals.net/prez1.jpg" width="35%"></a>
14
+ <a href="https://data.primals.net/prez6.jpg" target="_blank"><img alt="Light" src="https://data.primals.net/prez6.jpg" width="35%"></a>
15
+ <a href="https://data.primals.net/api-docs" target="_blank"><img alt="Dark" src="https://data.primals.net/prez5.jpg" width="25%"></a>
16
+ </p>
12
17
  ---
13
18
 
14
19
  ## 🚀 Key Features
@@ -23,18 +28,37 @@
23
28
  - **🌐 i18n support**: Multilingual interfaces, translated validations.
24
29
  - **📄 Auto Documentation**: Swagger available at `/api-docs`.
25
30
 
31
+
32
+ ## 🌟 Why Choose data-primals-engine?
33
+
34
+ - **Zero Boilerplate**: Focus on your business logic, not infrastructure
35
+ - **Scalability**: Architecture designed for rapidly growing applications
36
+ - **Modularity**: Enable/disable features as needed
37
+ - **Batteries Included**: Everything you need to get started quickly
38
+ - **Proven Performance**: Handles 50k+ documents efficiently
39
+ - **AI Ready**: Built-in LangChain integration for OpenAI/Gemini
40
+
26
41
  ---
27
42
 
28
43
  ## ⚙️ Requirements
29
44
 
30
- - Node.js ≥ 18
31
- - MongoDB (local or remote)
45
+ - Node.js ≥ 20
46
+ - MongoDB (local or remote), see [installation guide](https://www.mongodb.com/docs/manual/installation/)
32
47
  - NPM or Yarn
33
48
 
34
49
  ---
35
50
 
36
51
  ## ⚡ Quick Start
37
52
 
53
+ ### check
54
+ ```bash
55
+ # Verify required versions
56
+ node -v # Must show ≥ v18
57
+ mongod --version # Must be installed
58
+ ```
59
+
60
+ ### install
61
+
38
62
  ```bash
39
63
  npm i data-primals-engine
40
64
  ```
@@ -44,6 +68,8 @@ git clone https://github.com/anonympins/data-primals-engine.git
44
68
  cd data-primals-engine
45
69
  npm install
46
70
  ```
71
+
72
+ ### configure
47
73
  Possibly create a `.env` file:
48
74
  ```env
49
75
  MONGO_DB_URL=mongodb://127.0.0.1:27017
@@ -64,11 +90,11 @@ MONGO_DB_URL=mongodb://127.0.0.1:27017
64
90
  | SMTP_USER | Username for SMTP authentication. | user@example.com |
65
91
  | SMTP_PASS | Password for SMTP authentication. | password |
66
92
  | TLS | Encrypted connection (TLS) mode. Disabled by default | 0/1 false/true |
67
- | CERT | Path to cert file. | certs/ca.crt |
68
- | CA_CERT | Path to CA cert file. | certs/cert.pem |
93
+ | CA_CERT | Path to CA cert file. | certs/ca.crt |
94
+ | CERT | Path to cert file. | certs/cert.pem |
69
95
  | CERT_KEY | Path to the key file for your certificate. | certs/key.pem |
70
96
 
71
- Start the server:
97
+ ### Start the server
72
98
  ```bash
73
99
  # Development mode
74
100
  npm run devserver
@@ -77,7 +103,8 @@ npm run devserver
77
103
  npm run server
78
104
  ```
79
105
 
80
- By default, the app runs on port **7633**.
106
+ By default, the app runs on port **7633** : http://localhost:7633
107
+
81
108
 
82
109
  ---
83
110
 
@@ -93,10 +120,22 @@ Define schemas using JSON:
93
120
  { "name": "name", "type": "string", "required": true },
94
121
  { "name": "price", "type": "number", "required": true },
95
122
  { "name": "stock", "type": "number", "default": 0 },
96
- { "name": "category", "type": "relation", "relation": "taxonomy" }
123
+ { "name": "category", "type": "relation", "relation": "taxonomy",
124
+ "relationFilter": { "$eq": ["$type", "category"] }
125
+ },
126
+ { "name": "tags", "type": "relation", "relation": "taxonomy", "multiple": true,
127
+ "relationFilter": { "$eq": ["$type", "keyword"] }
128
+ }
97
129
  ]
98
130
  }
99
131
  ```
132
+ ### Smart Relations
133
+ - Handles up to 2,000 direct relations
134
+ - For larger datasets, use intermediate collections
135
+ - Automatic indexing on key fields
136
+ - Custom indexing on fields
137
+ - Custom fields :
138
+
100
139
  | Type | Description | Properties/Notes |
101
140
  |:------------|:------------------------------------------------------------------------------------|:--------------------------------------------------------------------------|
102
141
  | string | Character string. | minLength, maxLength |
@@ -124,10 +163,21 @@ Define schemas using JSON:
124
163
  Activatable features:
125
164
  - `mongodb`, `data`, `user`, `workflow`, `file`, `assistant`, `swagger`
126
165
 
127
- ### Starter Packs
128
- - **E-commerce**: Products, orders, KPIs
129
- - **CRM**: Contacts, leads, interactions
130
- - **Website/blog**: Pages, posts, i18n
166
+ ## 🏗️ Use Case Examples
167
+
168
+ ### 🛒 E-Commerce Backoffice
169
+ - Install ecommerce-starter pack
170
+ - Add products via API/UI
171
+ - Customize order workflows
172
+
173
+ ### 🎫 Support Ticket System
174
+ - Create ticket model with [open, pending, resolved] statuses
175
+ - Configure notification workflows
176
+ - Add custom endpoints or dashboards/kpi for analytics
177
+
178
+ ### 🤖 AI Chatbot
179
+ - Define your model
180
+ - Set up workflow: "When new entry → generate AI content"
131
181
 
132
182
  ---
133
183
 
@@ -297,7 +347,7 @@ await patchData(
297
347
  );
298
348
  ```
299
349
 
300
- ### deleteData(modelName, ids, filter, user)
350
+ ### deleteData(modelName, filter, user)
301
351
 
302
352
  >Deletes data with cascading relation cleanup.
303
353
 
@@ -305,13 +355,13 @@ Examples:
305
355
 
306
356
  ```javascript
307
357
  // Delete by IDs
308
- await deleteData("comments", ["61d1f1a9e3f1a9e3f1a9e3f1"], null, user);
358
+ await deleteData("comments", ["61d1f1a9e3f1a9e3f1a9e3f1"], user);
309
359
 
310
360
  // Delete by filter
311
- await deleteData("logs", null, { createdAt: { $lt: "2023-01-01" } }, user);
361
+ await deleteData("logs", { createdAt: { $lt: "2023-01-01" } }, user);
312
362
  ```
313
363
 
314
- ### searchData({user, query})
364
+ ### searchData(query, user)
315
365
 
316
366
  Powerful search with relation expansion and filtering.
317
367
 
@@ -326,15 +376,12 @@ Query Options:
326
376
  Example:
327
377
  ```javascript
328
378
  const results = await searchData({
329
- user: currentUser,
330
- query: {
331
- model: "blogPost",
332
- filter: { status: "published" },
333
- depth: 2, // Expand author and comments
334
- limit: 10,
335
- sort: "createdAt:DESC"
336
- }
337
- });
379
+ model: "blogPost",
380
+ filter: { status: "published" },
381
+ depth: 2, // Expand author and comments
382
+ limit: 10,
383
+ sort: "createdAt:DESC"
384
+ }, user);
338
385
  ```
339
386
 
340
387
  ## Import/Export
@@ -574,4 +621,4 @@ Distributed under the **MIT License**. See `LICENSE` file.
574
621
 
575
622
  ---
576
623
 
577
- ## [🔼](#) Back to Top
624
+ ## [🔼](https://github.com/anonympins/data-primals-engine?tab=readme-ov-file#data-primals-engine) Back to Top
@@ -1083,11 +1083,15 @@ h2.shadow {
1083
1083
  }
1084
1084
 
1085
1085
  .condition-details {
1086
- padding-left: 20px; // Indentation
1086
+ padding-left: 4px;
1087
+ margin-left:2px;
1088
+ @media only screen and (min-width: 480px) {
1089
+ padding-left: 20px; // Indentation
1090
+ margin-left: 10px;
1091
+ margin-top: 5px;
1092
+ margin-bottom: 10px;
1093
+ }
1087
1094
  border-left: 2px solid #eee; // Ligne visuelle
1088
- margin-left: 10px;
1089
- margin-top: 5px;
1090
- margin-bottom: 10px;
1091
1095
  gap: 5px; // Espacement entre les éléments de condition
1092
1096
  }
1093
1097
 
@@ -1290,9 +1294,10 @@ select[multiple]{
1290
1294
  }
1291
1295
 
1292
1296
  .datalayout {
1293
- padding: 0 16px;
1297
+ @media only screen and (min-width: 480px) {
1298
+ padding: 0 16px;
1299
+ }
1294
1300
  }
1295
-
1296
1301
  .main-menu {
1297
1302
  padding: 16px 0;
1298
1303
  @media only screen and (max-width: 479px) {
@@ -39,7 +39,6 @@ $input-height: 50px;
39
39
  bottom: 20px;
40
40
  right: 20px;
41
41
  width: 100%;
42
- left: 2px;
43
42
  max-width: 720px;
44
43
  min-height: 400px;
45
44
  height: 60vh;
@@ -1,4 +1,3 @@
1
- // C:/Dev/hackersonline-engine/client/src/ChartConfigModal.jsx
2
1
  import React, { useState, useEffect } from 'react';
3
2
  import { useTranslation } from 'react-i18next';
4
3
  import { useModelContext } from './contexts/ModelContext.jsx';
@@ -1,4 +1,3 @@
1
- // C:/Dev/hackersonline-engine/client/src/ConditionBuilder.jsx
2
1
 
3
2
  import React, {useState, useEffect, useRef} from 'react';
4
3
  import {
@@ -567,7 +566,6 @@ const ConditionBuilder = ({ initialValue = null, onChange, models, model = null,
567
566
  setRoot(p);
568
567
  onChange(p);
569
568
  };
570
- // C:/Dev/hackersonline-engine/client/src/ConditionBuilder.jsx
571
569
  useEffect(() => {
572
570
  let parsedValue = {}; // Valeur par défaut si tout échoue
573
571
 
@@ -8,6 +8,30 @@ import {FaRepeat} from "react-icons/fa6";
8
8
  import {useAuthContext} from "./contexts/AuthContext.jsx";
9
9
 
10
10
 
11
+ // Déterminer si le champ doit être une date
12
+ const isDateArg = (operator, argIndex) => {
13
+ if (!operator) return false;
14
+
15
+ const opConfig = MONGO_CALC_OPERATORS[operator];
16
+ if (!opConfig) {
17
+ console.warn(`Opérateur non trouvé: ${operator}`);
18
+ return false;
19
+ }
20
+
21
+ // Cas spécial pour les opérateurs de date
22
+ if (operator === '$dateAdd' || operator === '$dateSubtract') {
23
+ return argIndex === 0; // Seul le premier argument est une date
24
+ }
25
+
26
+ // Pour les autres opérateurs marqués isDate (comme $hour, $second, etc.)
27
+ if (opConfig.isDate) {
28
+ return true; // Tous les arguments sont des dates pour ces opérateurs
29
+ }
30
+
31
+ return false;
32
+ };
33
+
34
+
11
35
  const OperatorSelector = ({ onSelect }) => {
12
36
  const [search, setSearch] = useState('');
13
37
 
@@ -117,10 +141,12 @@ const ExpressionField = ({ value, onChange, path = [], fieldNames, isRoot = fals
117
141
  setEditing(false);
118
142
  };
119
143
 
144
+ console.log(path);
145
+ console.log(isDateArg(path[path.length - 1]));
120
146
 
121
147
  const renderArgument = (arg, index, parentOperator, args) => {
122
148
  const isSimpleValue = typeof arg !== 'object' || arg === null || Array.isArray(arg);
123
-
149
+ const parentOpConfig = MONGO_CALC_OPERATORS[parentOperator];
124
150
  // Fonction pour mettre à jour cet argument spécifique
125
151
  const handleArgChange = (newArgValue) => {
126
152
  const updatedArgs = [...args];
@@ -148,8 +174,6 @@ const ExpressionField = ({ value, onChange, path = [], fieldNames, isRoot = fals
148
174
  onChange({ [parentOperator]: updatedArgs }, path);
149
175
  }
150
176
  };
151
- const parentOpConfig = MONGO_CALC_OPERATORS[parentOperator];
152
-
153
177
  return (
154
178
  <div key={`arg-${index}`} className="expression-arg">
155
179
  <div className="arg-content">
@@ -163,10 +187,11 @@ const ExpressionField = ({ value, onChange, path = [], fieldNames, isRoot = fals
163
187
  updatedArgs[index] = newValue;
164
188
  onChange({ [parentOperator]: updatedArgs }, path);
165
189
  }}
190
+ isDate={isDateArg(path[path.length - 1], 0)} // Utilis
166
191
  fieldNames={fieldNames}
167
192
  isInFindContext={isInFindContext}
168
193
  />
169
- {!parentOpConfig?.disableAdvancedValue && (<button
194
+ {!parentOpConfig?.disableAdvancedValue && !isDateArg(path[path.length -1]) && (<button
170
195
  type="button"
171
196
  className="switch-to-expr"
172
197
  onClick={() => handleArgChange({ $eq: [arg, ""] })}
@@ -281,6 +306,7 @@ const ExpressionField = ({ value, onChange, path = [], fieldNames, isRoot = fals
281
306
  onChange={handleFieldChange}
282
307
  fieldNames={fieldNames}
283
308
  isFieldName={true}
309
+ isDate={isDateArg(path[path.length - 1])}
284
310
  currentModelFields={currentModelFields} // Passer les champs complets
285
311
  onlyRelations={true}
286
312
  />
@@ -607,12 +633,13 @@ const ExpressionField = ({ value, onChange, path = [], fieldNames, isRoot = fals
607
633
  }}
608
634
  fieldNames={fieldNames}
609
635
  isInFindContext={isInFindContext}
636
+ isDate={isDateArg(path[path.length - 1])}
610
637
  currentModelFields={currentModelFields} // On propage le contexte
611
638
  />
612
639
 
613
640
  {/* --- MODIFICATION --- */}
614
641
  {/* On affiche le bouton de conversion uniquement si l'opérateur parent le permet */}
615
- {parentOpConfig?.disableAdvancedValue !== true && (
642
+ {parentOpConfig?.disableAdvancedValue !== true && !isDateArg(path[path.length - 1]) && (
616
643
  <button
617
644
  type="button"
618
645
  className="switch-to-expr"
@@ -637,11 +664,13 @@ const FieldInput = ({
637
664
  isFieldName = false,
638
665
  isInFindContext = false,
639
666
  currentModelFields = [],
640
- onlyRelations = false
667
+ onlyRelations = false,
668
+ isDate = false
641
669
  }) => {
642
670
  const [inputValue, setInputValue] = useState(value);
643
671
  const [suggestions, setSuggestions] = useState([]);
644
672
  const [showSuggestions, setShowSuggestions] = useState(false);
673
+ const [showDatePicker, setShowDatePicker] = useState(false);
645
674
 
646
675
  useEffect(() => {
647
676
  setInputValue(value);
@@ -685,6 +714,18 @@ const FieldInput = ({
685
714
  setShowSuggestions(false);
686
715
  };
687
716
 
717
+ // Si c'est un champ date, on affiche le datepicker
718
+ if (isDate) {
719
+ return (
720
+ <div className="date-input-container">
721
+ <input
722
+ type="datetime-local"
723
+ value={inputValue ? new Date(inputValue).toISOString().slice(0, 16) : ''}
724
+ onChange={(e) => handleDateChange(e.target.value)}
725
+ />
726
+ </div>
727
+ );
728
+ }
688
729
  return (
689
730
  <div className="field-input-container">
690
731
  <input
@@ -1,4 +1,3 @@
1
- // C:/Dev/hackersonline-engine/client/src/pages/ContentView.jsx
2
1
  import React, {useEffect, useMemo, useRef, useState} from 'react';
3
2
  import {useParams, Link, useLocation } from 'react-router-dom'; // NavLink supprimé car non utilisé
4
3
  import { useQuery } from 'react-query';
@@ -1,4 +1,3 @@
1
- // C:/Dev/hackersonline-engine/client/src/CronBuilder.scss
2
1
 
3
2
  // J'utilise ici des variables pour les couleurs et espacements.
4
3
  // Si votre projet a déjà un fichier de variables SCSS, il serait bon de les utiliser
@@ -1,4 +1,3 @@
1
- // C:/Dev/hackersonline-engine/client/src/CronPartBuilder.jsx
2
1
 
3
2
  import React, { useMemo } from 'react';
4
3
  import { SelectField, TextField, NumberField } from './Field.jsx';
@@ -1,4 +1,3 @@
1
- // C:/Dev/hackersonline-engine/client/src/Dashboard.jsx
2
1
  import React, {useState, useMemo, useEffect} from 'react';
3
2
  import { Trans, useTranslation } from 'react-i18next';
4
3
  import {FaPlus, FaSpinner} from "react-icons/fa"; // Ajout FaTrash
@@ -231,7 +231,6 @@
231
231
  }
232
232
  }
233
233
  }
234
- // C:/Dev/hackersonline-engine/client/src/Dashboard.scss
235
234
  // ... (autres styles existants) ...
236
235
 
237
236
  // Styles pour les badges de données dans FlexView
@@ -1,4 +1,3 @@
1
- // C:/Dev/hackersonline-engine/client/src/DashboardChart.jsx
2
1
  import React, {useEffect, useMemo, useState} from 'react';
3
2
  import { fr } from 'date-fns/locale'
4
3
  import {
@@ -1,4 +1,3 @@
1
- // C:/Dev/hackersonline-engine/client/src/DashboardView.jsx
2
1
  import React, {useEffect, useState, useMemo, useRef, useCallback} from 'react';
3
2
  import { Trans, useTranslation } from 'react-i18next';
4
3
  import KPIWidget from "./KPIWidget.jsx";
@@ -52,10 +52,6 @@ const getInputType = (fieldType) => {
52
52
  }
53
53
  };
54
54
 
55
- // C:/Dev/hackersonline-engine/client/src/DataEditor.jsx
56
- // C:/Dev/hackersonline-engine/client/src/DataEditor.jsx
57
-
58
-
59
55
  /**
60
56
  * Evaluates a single condition against form data.
61
57
  * @param {object} currentModelDef - The definition of the current model.
@@ -1,4 +1,3 @@
1
- // C:/Dev/hackersonline-engine/client/src/DisplayFlexNodeRenderer.jsx
2
1
  import {useTranslation} from "react-i18next";
3
2
  import {cssProps} from "../../src/core.js";
4
3
  import FlexDataRenderer from "./FlexDataRenderer.jsx";
@@ -1,8 +1,6 @@
1
- // C:/Dev/hackersonline-engine/client/src/DocumentationPageLayout.jsx
2
1
  import React from 'react';
3
2
  import { Outlet } from 'react-router-dom';
4
3
  import './DocumentationPageLayout.scss'; // Nous créerons ce fichier SCSS ensuite
5
- // C:/Dev/hackersonline-engine/client/src/DocumentationMenu.jsx
6
4
  import { NavLink } from 'react-router-dom';
7
5
  import { useTranslation } from 'react-i18next';
8
6
  import { FaChevronDown, FaChevronRight } from 'react-icons/fa';
@@ -1,4 +1,3 @@
1
- // C:/Dev/hackersonline-engine/client/src/DocumentationPageLayout.scss
2
1
  @import "_variables";
3
2
 
4
3
  .documentation-layout {
@@ -493,7 +493,7 @@ const CheckboxField = forwardRef(
493
493
  };
494
494
  return (
495
495
  <>
496
- <div className={cn({field: true, "field-checkbox": true})}>
496
+ <div className={cn({field: true, "field-checkbox": true,"field-bg": true})}>
497
497
  <div className="inline"><input
498
498
  aria-required={required}
499
499
  aria-readonly={readOnly}
@@ -1,4 +1,3 @@
1
- // C:/Dev/hackersonline-engine/client/src/FlexNode.jsx
2
1
  import React, {useEffect, useRef, useState} from 'react';
3
2
  import PropTypes from 'prop-types';
4
3
  import {
@@ -1,4 +1,3 @@
1
- // C:/Dev/hackersonline-engine/client/src/KanbanView.scss
2
1
 
3
2
  // --- Variables pour une personnalisation facile ---
4
3
  $board-bg: #f4f5f7; // Fond gris clair pour le tableau
@@ -526,7 +526,7 @@ const ModelCreator = forwardRef(({ initialPrompt = '', onModelGenerated, autoGen
526
526
  {/* Colonne de droite: Formulaire du modèle sélectionné */}
527
527
  <div className="model-form-container">
528
528
  {/* Le formulaire existant est placé ici */}
529
- <div className="flex">
529
+ <div className="flex field-bg">
530
530
  <label htmlFor="modelName"><Trans i18nKey={"modelcreator.name"}>Nom:</Trans></label>
531
531
  </div>
532
532
  <TextField
@@ -539,7 +539,7 @@ const ModelCreator = forwardRef(({ initialPrompt = '', onModelGenerated, autoGen
539
539
  required
540
540
  />
541
541
 
542
- <div className="flex">
542
+ <div className="flex field-bg">
543
543
  <label htmlFor="modelDescription"><Trans i18nKey={"modelcreator.description"}>Description:</Trans></label>
544
544
  </div>
545
545
  <TextField
@@ -570,7 +570,7 @@ const ModelCreator = forwardRef(({ initialPrompt = '', onModelGenerated, autoGen
570
570
  {/* Affichage du formulaire en mode manuel ou édition */}
571
571
  {(!useAI || initialModel) && (
572
572
  <div className="model-form-container">
573
- <div className="flex">
573
+ <div className="flex field-bg">
574
574
  <label htmlFor="modelName"><Trans i18nKey={"modelcreator.name"}>Nom:</Trans></label>
575
575
  </div>
576
576
  <TextField
@@ -583,7 +583,7 @@ const ModelCreator = forwardRef(({ initialPrompt = '', onModelGenerated, autoGen
583
583
  required
584
584
  />
585
585
 
586
- <div className="flex">
586
+ <div className="flex field-bg">
587
587
  <label htmlFor="modelDescription"><Trans i18nKey={"modelcreator.description"}>Description:</Trans></label>
588
588
  </div>
589
589
  <TextField
@@ -2,6 +2,7 @@
2
2
  flex: 1;
3
3
  max-width: 720px;
4
4
  form {
5
+ padding: 0;
5
6
  @media only screen and (min-width: 480px){
6
7
  padding: 16px;
7
8
  }
@@ -151,4 +152,13 @@
151
152
  }
152
153
  }
153
154
  }
155
+ }
156
+
157
+ .field-bg {
158
+ background: #fcfcfc;
159
+ background: linear-gradient(180deg, rgba(252, 252, 252, 1) 0%, rgba(242, 242, 242, 1) 55%, rgba(242, 242, 242, 1) 100%);
160
+ }
161
+
162
+ .mg-item{
163
+ margin: 10px 0;
154
164
  }