data-primals-engine 1.0.11 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/README.md +8 -0
- package/client/index.html +31 -0
- package/client/index.js +25 -0
- package/client/package-lock.json +11664 -0
- package/client/package.json +70 -0
- package/client/public/demo/26899917-d1ba-4df4-bb33-48d09a8778da.jpg +0 -0
- package/client/public/demo/4b9894c7-12cd-466d-8fd3-a2757b09ec96.jpg +0 -0
- package/client/public/demo/7ed369ac-a1a2-4b45-b0cd-4fd5bcf5a75a.jpg +0 -0
- package/client/public/doc/API.postman_collection.json +214 -0
- package/client/public/github.svg +1 -0
- package/client/public/profilCompany.jpg +0 -0
- package/client/public/profilDeveloper.jpg +0 -0
- package/client/public/profilEngineer.jpg +0 -0
- package/client/public/profilPersonal.jpg +0 -0
- package/client/public/profils .psd +0 -0
- package/client/public/react.svg +9 -0
- package/client/src/APIInfo.jsx +328 -0
- package/client/src/AddWidgetTypeModal.jsx +44 -0
- package/client/src/AddWidgetTypeModal.scss +87 -0
- package/client/src/App.css +42 -0
- package/client/src/App.jsx +586 -0
- package/client/src/App.scss +1466 -0
- package/client/src/AssistantChat.jsx +280 -0
- package/client/src/AssistantChat.scss +229 -0
- package/client/src/Button.jsx +12 -0
- package/client/src/Button.scss +197 -0
- package/client/src/CalculationBuilder.jsx +338 -0
- package/client/src/CalculationStepRow.jsx +199 -0
- package/client/src/CalendarConfigModal.jsx +49 -0
- package/client/src/ChartConfigModal.jsx +402 -0
- package/client/src/ConditionBuilder.jsx +622 -0
- package/client/src/ConditionBuilder.scss +672 -0
- package/client/src/ConditionBuilder2.jsx +757 -0
- package/client/src/ContentView.jsx +274 -0
- package/client/src/CronBuilder.jsx +126 -0
- package/client/src/CronBuilder.scss +128 -0
- package/client/src/CronPartBuilder.jsx +150 -0
- package/client/src/Dashboard.jsx +210 -0
- package/client/src/Dashboard.scss +445 -0
- package/client/src/DashboardChart.jsx +362 -0
- package/client/src/DashboardFlexViewItem.jsx +129 -0
- package/client/src/DashboardView.jsx +554 -0
- package/client/src/DataChart.jsx +349 -0
- package/client/src/DataEditor.jsx +556 -0
- package/client/src/DataLayout.jsx +709 -0
- package/client/src/DataTable.jsx +996 -0
- package/client/src/DataTable.scss +73 -0
- package/client/src/Dialog.jsx +90 -0
- package/client/src/Dialog.scss +117 -0
- package/client/src/DisplayFlexNodeRenderer.jsx +115 -0
- package/client/src/DocumentationPageLayout.jsx +93 -0
- package/client/src/DocumentationPageLayout.scss +262 -0
- package/client/src/Draggable.jsx +222 -0
- package/client/src/Draggable.scss +49 -0
- package/client/src/ExportDialog.jsx +172 -0
- package/client/src/Field.jsx +1558 -0
- package/client/src/FlexBuilder.jsx +269 -0
- package/client/src/FlexBuilder.scss +226 -0
- package/client/src/FlexBuilderControls.jsx +170 -0
- package/client/src/FlexBuilderModal.jsx +75 -0
- package/client/src/FlexBuilderModal.scss +109 -0
- package/client/src/FlexBuilderPreview.jsx +48 -0
- package/client/src/FlexBuilderUtils.js +84 -0
- package/client/src/FlexDataRenderer.jsx +170 -0
- package/client/src/FlexDataRenderer.scss +80 -0
- package/client/src/FlexNode.jsx +290 -0
- package/client/src/FlexTreeUtils.js +129 -0
- package/client/src/HiddenableCell.jsx +46 -0
- package/client/src/HiddenableCell.scss +36 -0
- package/client/src/KPIDialog.jsx +39 -0
- package/client/src/KPIWidget.jsx +139 -0
- package/client/src/KanbanCard.jsx +92 -0
- package/client/src/KanbanColumn.jsx +29 -0
- package/client/src/KanbanConfigModal.jsx +115 -0
- package/client/src/KanbanView.jsx +113 -0
- package/client/src/KanbanView.scss +107 -0
- package/client/src/MessageRotator.jsx +77 -0
- package/client/src/MessageRotator.scss +14 -0
- package/client/src/ModelCreator.jsx +640 -0
- package/client/src/ModelCreator.scss +154 -0
- package/client/src/ModelCreatorField.jsx +810 -0
- package/client/src/ModelImporter.jsx +120 -0
- package/client/src/ModelList.jsx +205 -0
- package/client/src/Notification.jsx +137 -0
- package/client/src/Notification.scss +126 -0
- package/client/src/NotificationProvider.jsx +73 -0
- package/client/src/PackGallery.jsx +211 -0
- package/client/src/PackGallery.scss +156 -0
- package/client/src/Pagination.jsx +141 -0
- package/client/src/RTE.jsx +415 -0
- package/client/src/RTETrans.jsx +103 -0
- package/client/src/RelationField.jsx +247 -0
- package/client/src/RelationValue.jsx +230 -0
- package/client/src/RestoreConfirmationModal.jsx +183 -0
- package/client/src/RestoreDialog.jsx +134 -0
- package/client/src/RestoreDialog.scss +67 -0
- package/client/src/RichTextEditorModal.jsx +34 -0
- package/client/src/RichTextEditorModal.scss +54 -0
- package/client/src/TourSpotlight.jsx +234 -0
- package/client/src/TourSpotlight.scss +100 -0
- package/client/src/TutorialsMenu.jsx +83 -0
- package/client/src/TutorialsMenu.scss +217 -0
- package/client/src/TutorialsRewardModal.jsx +65 -0
- package/client/src/Utils.jsx +78 -0
- package/client/src/ViewSwitcher.jsx +54 -0
- package/client/src/ViewSwitcher.scss +74 -0
- package/client/src/Webpage.jsx +69 -0
- package/client/src/_variables.scss +21 -0
- package/client/src/assets/react.svg +1 -0
- package/client/src/constants.js +68 -0
- package/client/src/contexts/AuthContext.jsx +28 -0
- package/client/src/contexts/ModelContext.jsx +328 -0
- package/client/src/contexts/UIContext.jsx +52 -0
- package/client/src/core/data.js +35 -0
- package/client/src/entry-client.jsx +15 -0
- package/client/src/entry-server.jsx +13 -0
- package/client/src/filter.js +222 -0
- package/client/src/hooks/data.js +101 -0
- package/client/src/hooks/useDebounce.js +20 -0
- package/client/src/hooks/useDragAndDrop.js +59 -0
- package/client/src/hooks/useLocalStorage.js +100 -0
- package/client/src/hooks/useMount.js +42 -0
- package/client/src/hooks/useTutorials.jsx +227 -0
- package/client/src/hooks/useWindowSize.js +24 -0
- package/client/src/i18n.js +47 -0
- package/client/src/index.css +90 -0
- package/client/src/index.js +10 -0
- package/client/src/main.jsx +11 -0
- package/client/src/translations.js +15940 -0
- package/client/src/tutorials.js +112 -0
- package/client/vite.config.js +30 -0
- package/eslint.config.js +50 -0
- package/package.json +48 -27
- package/server.js +9 -0
- package/src/constants.js +12 -23
- package/src/core.js +9 -8
- package/src/data.js +81 -80
- package/src/defaultModels.js +114 -115
- package/src/email.js +8 -9
- package/src/engine.js +5 -5
- package/src/events.js +1 -2
- package/src/i18n.js +212 -211
- package/src/middlewares/middleware-mongodb.js +94 -93
- package/src/migrate.js +24 -24
- package/src/modules/assistant.js +55 -55
- package/src/modules/bucket.js +10 -9
- package/src/modules/data.js +387 -357
- package/src/modules/file.js +3 -6
- package/src/modules/mongodb.js +6 -7
- package/src/modules/user.js +24 -47
- package/src/modules/workflow.js +68 -67
- package/src/openai.jobs.js +2 -2
- package/src/packs.js +30 -31
- package/src/providers.js +101 -3
- package/src/setenv.js +1 -1
- package/src/user.js +0 -17
- package/src/workers/crypto-worker.js +21 -20
- package/src/workers/import-export-worker.js +18 -18
- package/test/data.backup.integration.test.js +7 -12
- package/test/data.integration.test.js +27 -25
- package/test/globalSetup.js +2 -2
- package/test/import_export.integration.test.js +6 -6
- package/test/model.integration.test.js +21 -22
- package/test/workflow.integration.test.js +2 -5
- package/test/workflow.robustness.test.js +6 -13
- package/vitest.config.js +2 -2
|
@@ -0,0 +1,810 @@
|
|
|
1
|
+
import React, {useState} from "react";
|
|
2
|
+
import {useModelContext} from "./contexts/ModelContext.jsx";
|
|
3
|
+
import {useAuthContext} from "./contexts/AuthContext.jsx";
|
|
4
|
+
import {isLocalUser} from "data-primals-engine/data";
|
|
5
|
+
import {Trans, useTranslation} from "react-i18next";
|
|
6
|
+
import {FaCircleInfo} from "react-icons/fa6";
|
|
7
|
+
import {CheckboxField, CodeField, ColorField, NumberField, SelectField, TextField} from "./Field.jsx";
|
|
8
|
+
import Button from "./Button.jsx";
|
|
9
|
+
import {FaArrowDown, FaArrowUp, FaEdit, FaMinus, FaPlus, FaTrash} from "react-icons/fa";
|
|
10
|
+
import CalculationBuilder from "./CalculationBuilder.jsx";
|
|
11
|
+
import ConditionBuilder from "./ConditionBuilder.jsx";
|
|
12
|
+
import Draggable from "./Draggable.jsx";
|
|
13
|
+
import {mainFieldsTypes} from "data-primals-engine/constants";
|
|
14
|
+
|
|
15
|
+
function RelationModelSelector({relation, onChange, ...rest}) {
|
|
16
|
+
const {models} = useModelContext()
|
|
17
|
+
const {me} = useAuthContext()
|
|
18
|
+
const {t} = useTranslation();
|
|
19
|
+
return (
|
|
20
|
+
<select {...rest} onChange={(e) => onChange(e.target.value)}>
|
|
21
|
+
<option value=""><Trans i18nKey={"relation.selectModel"}>Sélectionner un modèle</Trans></option>
|
|
22
|
+
{models.filter(f => f._user === me?.username).map((model) => (
|
|
23
|
+
<option selected={model.name === relation} key={model.name} value={model.name}>
|
|
24
|
+
{t(`model_${model.name}`, model.name)}
|
|
25
|
+
</option>
|
|
26
|
+
))}
|
|
27
|
+
</select>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
const ValueField = ({v, setValue, disabled}) => {
|
|
33
|
+
const {t} = useTranslation();
|
|
34
|
+
return <TextField value={v} placeholder={t('modelcreator.field.value', 'Valeur')} disabled={disabled} onChange={(e) => setValue(e.target.value)} />
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const ModelCreatorField = ({model, handleRenameField, handleRemoveField, handleUp, handleDown, handleRemoveValue, handleAddValue, setFields, fields, field, index}) => {
|
|
38
|
+
|
|
39
|
+
const {models} = useModelContext()
|
|
40
|
+
const {me } = useAuthContext()
|
|
41
|
+
const modelLocked = !!model && (!model?._user ? true : isLocalUser(me) && model?.locked);
|
|
42
|
+
const { t, i18n } = useTranslation();
|
|
43
|
+
const [showMore, setMoreVisible] = useState(false);
|
|
44
|
+
const hint = (description) => t(description, '') && <div className="hint-icon"><FaCircleInfo data-tooltip-id={`tooltipHint`} data-tooltip-content={description} /></div>
|
|
45
|
+
|
|
46
|
+
const [focusedField, setFocusedField] = useState({});
|
|
47
|
+
return (
|
|
48
|
+
<div className="field-edit">
|
|
49
|
+
|
|
50
|
+
<div className="actions right">
|
|
51
|
+
{index < fields.length - 1 && (
|
|
52
|
+
<button title={"Déplacer vers le bas"} type="button"
|
|
53
|
+
onClick={() => handleDown(index)}>
|
|
54
|
+
<FaArrowDown/>
|
|
55
|
+
</button>)}
|
|
56
|
+
{index > 0 && (
|
|
57
|
+
<button title="Déplacer vers le haut" type="button"
|
|
58
|
+
onClick={() => handleUp(index)}>
|
|
59
|
+
<FaArrowUp/>
|
|
60
|
+
</button>)}
|
|
61
|
+
</div>
|
|
62
|
+
<div className="flex flex-row flex-stretch">
|
|
63
|
+
|
|
64
|
+
<div className="flex fieldName">{hint('modelcreator.name.hint')}
|
|
65
|
+
<div className="flex flex-no-gap flex-no-wrap flex-1">
|
|
66
|
+
<TextField
|
|
67
|
+
label={t('modelcreator.fieldName')}
|
|
68
|
+
className={!modelLocked && !field._isNewField && "input-fit"}
|
|
69
|
+
disabled={modelLocked || (!!model && !field._isNewField)}
|
|
70
|
+
value={field.name}
|
|
71
|
+
onChange={(e) => {
|
|
72
|
+
const newFields = [...fields];
|
|
73
|
+
newFields[index].name = e.target.value;
|
|
74
|
+
setFields(newFields);
|
|
75
|
+
}}
|
|
76
|
+
help={focusedField?.name === field.name && t('modelcreator.name.hint')}
|
|
77
|
+
onFocus={() => setFocusedField(field)}
|
|
78
|
+
onBlur={() => setFocusedField(null)}
|
|
79
|
+
required
|
|
80
|
+
/>
|
|
81
|
+
{!(!modelLocked && isLocalUser(me) && field.locked) && !field._isNewField && (
|
|
82
|
+
<Button type={"button"} className={"btn-form btn-last"}
|
|
83
|
+
onClick={() => handleRenameField(index, field.name)}><FaEdit/></Button>)}
|
|
84
|
+
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
<div className="flex">
|
|
90
|
+
{hint('modelcreator.type.hint')}
|
|
91
|
+
<div className="flex flex-1 flex-stretch flex-no-gap">
|
|
92
|
+
|
|
93
|
+
<SelectField
|
|
94
|
+
label={t('modelcreator.type', 'Type de champ')}
|
|
95
|
+
className={"flex-1"}
|
|
96
|
+
value={field.type}
|
|
97
|
+
onChange={(e) => {
|
|
98
|
+
const newFields = [...fields];
|
|
99
|
+
|
|
100
|
+
const nf = newFields[index];
|
|
101
|
+
nf.type = e.value;
|
|
102
|
+
|
|
103
|
+
setFields(newFields);
|
|
104
|
+
gtag('event', 'model set field ' + e.value);
|
|
105
|
+
}}
|
|
106
|
+
items={[
|
|
107
|
+
{label: t("field.string"), value: "string"},
|
|
108
|
+
{label: t("field.string_t"), value: "string_t"},
|
|
109
|
+
{label: t("field.richtext"), value: "richtext"},
|
|
110
|
+
{label: t("field.richtext_t"), value: "richtext_t"},
|
|
111
|
+
{label: t("field.number"), value: "number"},
|
|
112
|
+
{label: t("field.boolean"), value: "boolean"},
|
|
113
|
+
{label: t("field.enum"), value: "enum"},
|
|
114
|
+
{label: t("field.date"), value: "date"},
|
|
115
|
+
{label: t("field.datetime"), value: "datetime"},
|
|
116
|
+
{label: t("field.password"), value: "password"},
|
|
117
|
+
{label: t("field.email"), value: "email"},
|
|
118
|
+
{label: t("field.phone"), value: "phone"},
|
|
119
|
+
{label: t("field.url"), value: "url"},
|
|
120
|
+
{label: t("field.color"), value: "color"},
|
|
121
|
+
{label: t("field.array"), value: "array"},
|
|
122
|
+
{label: t("field.relation"), value: "relation"},
|
|
123
|
+
{label: t("field.file"), value: "file"},
|
|
124
|
+
{label: t("field.code"), value: "code"},
|
|
125
|
+
{label: t("field.calculated", "calculated data"), value: "calculated"},
|
|
126
|
+
{label: t("field.cronSchedule", "task schedule"), value: "cronSchedule"},
|
|
127
|
+
]}
|
|
128
|
+
/>
|
|
129
|
+
{/* Afficher le sélecteur de modèle lié si le type est "relation" */}
|
|
130
|
+
|
|
131
|
+
{(field.type === 'relation' || field.type === 'array') && <div className={"div"}>
|
|
132
|
+
{/* Afficher le sélecteur de modèle lié si le type est "relation" */}
|
|
133
|
+
{field.type === 'relation' && (
|
|
134
|
+
<>
|
|
135
|
+
<RelationModelSelector
|
|
136
|
+
className={"flex-1 mg-v-1"}
|
|
137
|
+
relation={field.relation}
|
|
138
|
+
onChange={(relation) => {
|
|
139
|
+
const newFields = [...fields];
|
|
140
|
+
newFields[index].relation = relation;
|
|
141
|
+
setFields(newFields);
|
|
142
|
+
|
|
143
|
+
}}
|
|
144
|
+
/>
|
|
145
|
+
</>
|
|
146
|
+
)}
|
|
147
|
+
{/* Afficher le sélecteur de modèle lié si le type est "relation" */}
|
|
148
|
+
{field.type === 'array' && (
|
|
149
|
+
<>
|
|
150
|
+
<select
|
|
151
|
+
className={"flex-1 mg-v-1"}
|
|
152
|
+
value={field.itemsType}
|
|
153
|
+
onChange={(e) => {
|
|
154
|
+
const newFields = [...fields];
|
|
155
|
+
newFields[index].itemsType = e.target.value;
|
|
156
|
+
setFields(newFields);
|
|
157
|
+
|
|
158
|
+
gtag('event', 'model set itemsType ' + e.target.value);
|
|
159
|
+
}}
|
|
160
|
+
>
|
|
161
|
+
<option value="string"><Trans i18nKey={"field.string"}>Texte</Trans></option>
|
|
162
|
+
<option value="string_t"><Trans i18nKey={"field.string_t"}>Texte traduit</Trans>
|
|
163
|
+
</option>
|
|
164
|
+
<option value="richtext"><Trans i18nKey={"field.richtext"}>Texte enrichi</Trans>
|
|
165
|
+
</option>
|
|
166
|
+
<option value="richtext_t"><Trans i18nKey={"field.richtext_t"}>Texte enrichi
|
|
167
|
+
traduit</Trans>
|
|
168
|
+
</option>
|
|
169
|
+
<option value="number"><Trans i18nKey={"field.number"}>Nombre</Trans></option>
|
|
170
|
+
<option value="boolean"><Trans i18nKey={"field.boolean"}>Booléen</Trans>
|
|
171
|
+
</option>
|
|
172
|
+
<option value="date"><Trans i18nKey={"field.date"}>Date</Trans></option>
|
|
173
|
+
<option value="datetime"><Trans i18nKey={"field.datetime"}>Date / Heure</Trans>
|
|
174
|
+
</option>
|
|
175
|
+
<option value="file"><Trans i18nKey={"field.file"}>Fichier</Trans>
|
|
176
|
+
</option>
|
|
177
|
+
<option value="email"><Trans i18nKey={"field.email"}>Email</Trans></option>
|
|
178
|
+
<option value="phone"><Trans i18nKey={"field.phone"}>Téléphone</Trans></option>
|
|
179
|
+
<option value="url"><Trans i18nKey={"field.url"}>URL</Trans></option>
|
|
180
|
+
<option value="color"><Trans i18nKey={"field.color"}>Couleur</Trans></option>
|
|
181
|
+
<option value="code"><Trans i18nKey={"field.code"}>Code</Trans></option>
|
|
182
|
+
<option value="calculated"><Trans i18nKey={"field.calculated"}>Calculated
|
|
183
|
+
data</Trans></option>
|
|
184
|
+
<option value="cronSchedule"><Trans i18nKey={"field.cronSchedule"}>Task
|
|
185
|
+
schedule</Trans></option>
|
|
186
|
+
{/* Ajoutez d'autres types de champs ici */}
|
|
187
|
+
</select>
|
|
188
|
+
</>
|
|
189
|
+
)}
|
|
190
|
+
</div>}
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
|
|
194
|
+
<div key={index + field.name}>
|
|
195
|
+
{field.type === 'calculated' && (
|
|
196
|
+
<>
|
|
197
|
+
<CalculationBuilder
|
|
198
|
+
// Clé : Assurez-vous que modelName est utilisé si model n'existe pas (nouveau modèle)
|
|
199
|
+
key={`${model?.name || modelName}-${field.name}-calc`}
|
|
200
|
+
// Prop AJOUTÉE : Nom du modèle en cours de création/modification
|
|
201
|
+
currentModelName={model?.name}
|
|
202
|
+
// Champs disponibles du modèle actuel (on filtre le champ calculé lui-même)
|
|
203
|
+
availableFields={fields.filter(f => f.name !== field.name)}
|
|
204
|
+
// Tous les modèles (pour les relations)
|
|
205
|
+
models={models} // Vient de useModelContext(), semble correct
|
|
206
|
+
initialSteps={field.calculation?.steps || []}
|
|
207
|
+
onCalculationChange={(calc) => {
|
|
208
|
+
const newFields = [...fields];
|
|
209
|
+
console.log({calc});
|
|
210
|
+
newFields[index].calculation = calc; // 'index' est l'index du champ calculé dans 'fields'
|
|
211
|
+
setFields(newFields);
|
|
212
|
+
}}
|
|
213
|
+
/>
|
|
214
|
+
</>
|
|
215
|
+
)}
|
|
216
|
+
{field.type === "relation" && (
|
|
217
|
+
|
|
218
|
+
<div className="flex flex-no-wrap">
|
|
219
|
+
{hint('modelcreator.relationFilter.hint')}
|
|
220
|
+
<label className="checkbox-label flex flex-1"><Trans
|
|
221
|
+
i18nKey={"modelcreator.relationFilter"}>Filtre</Trans> :
|
|
222
|
+
<input
|
|
223
|
+
type="checkbox"
|
|
224
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
225
|
+
checked={field.relationFilter !== undefined}
|
|
226
|
+
onChange={(e) => {
|
|
227
|
+
const newFields = [...fields];
|
|
228
|
+
if (e.target.checked) {
|
|
229
|
+
newFields[index].relationFilter = null;
|
|
230
|
+
} else {
|
|
231
|
+
delete newFields[index].relationFilter;
|
|
232
|
+
}
|
|
233
|
+
setFields(newFields);
|
|
234
|
+
}}
|
|
235
|
+
/>
|
|
236
|
+
</label>
|
|
237
|
+
</div>)}
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
{field.relationFilter !== undefined && Array.isArray(models) && (
|
|
241
|
+
<div className={"condition-details flex flex-start"}>
|
|
242
|
+
<ConditionBuilder
|
|
243
|
+
modelFields={models.find(f => f.name === field.relation)?.fields || []}
|
|
244
|
+
model={field.relation}
|
|
245
|
+
models={models}
|
|
246
|
+
selectableModels={false}
|
|
247
|
+
initialValue={field.relationFilter}
|
|
248
|
+
onChange={(newCondition) => {
|
|
249
|
+
const newFields = [...fields];
|
|
250
|
+
newFields[index].relationFilter = newCondition || {};
|
|
251
|
+
setFields(newFields);
|
|
252
|
+
}}
|
|
253
|
+
/>
|
|
254
|
+
</div>
|
|
255
|
+
)}
|
|
256
|
+
|
|
257
|
+
{(field.itemsType || field.type) === 'file' && (
|
|
258
|
+
<>
|
|
259
|
+
<div className={"flex"}>
|
|
260
|
+
{hint('modelcreator.mimeTypes.hint')}
|
|
261
|
+
<label className={"flex"}>
|
|
262
|
+
<Trans i18nKey={"modelcreator.mimeTypes"}>Types de fichiers
|
|
263
|
+
acceptés
|
|
264
|
+
:</Trans>
|
|
265
|
+
<input
|
|
266
|
+
type="text"
|
|
267
|
+
value={field.mimeTypes}
|
|
268
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
269
|
+
placeholder={t('modelcreator.field.mimeTypes.ph', "image/png, image/jpeg...")}
|
|
270
|
+
onChange={(e) => {
|
|
271
|
+
const newFields = [...fields];
|
|
272
|
+
newFields[index].mimeTypes = e.target.value.split(',').map(m => m.trim());
|
|
273
|
+
setFields(newFields);
|
|
274
|
+
}}
|
|
275
|
+
/>
|
|
276
|
+
</label></div>
|
|
277
|
+
</>
|
|
278
|
+
)}
|
|
279
|
+
|
|
280
|
+
{['string', 'string_t'].includes(field.itemsType || field.type) && (
|
|
281
|
+
<>
|
|
282
|
+
<div className="flex flex-no-wrap">
|
|
283
|
+
{hint('modelcreator.multiline.hint')}
|
|
284
|
+
<div className={"checkbox-label flex flex-1"}>
|
|
285
|
+
<CheckboxField
|
|
286
|
+
label={<Trans i18nKey={"modelcreator.multiline"}>Multi-lignes :</Trans>}
|
|
287
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
288
|
+
checked={field.multiline}
|
|
289
|
+
onChange={(e) => {
|
|
290
|
+
const newFields = [...fields];
|
|
291
|
+
newFields[index].multiline = e.target.checked;
|
|
292
|
+
setFields(newFields);
|
|
293
|
+
}}
|
|
294
|
+
help={field.multiline && t('modelcreator.multiline.hint')}
|
|
295
|
+
/>
|
|
296
|
+
</div>
|
|
297
|
+
</div>
|
|
298
|
+
</>
|
|
299
|
+
)}
|
|
300
|
+
|
|
301
|
+
{['string', 'string_t', 'richtext', 'richtext_t', 'email', 'phone', 'url', 'password', 'code'].includes(field.itemsType || field.type) && (
|
|
302
|
+
<>
|
|
303
|
+
<div className={"flex flex-no-wrap"}>
|
|
304
|
+
{hint('modelcreator.maxlength.hint')}
|
|
305
|
+
<label className={"flex flex-1"}>
|
|
306
|
+
<Trans i18nKey={"modelcreator.maxlength"}>Longueur
|
|
307
|
+
maximale :</Trans>
|
|
308
|
+
<NumberField
|
|
309
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
310
|
+
step={1}
|
|
311
|
+
min={0}
|
|
312
|
+
className={"flex-1"}
|
|
313
|
+
value={field.maxlength}
|
|
314
|
+
onChange={(e) => {
|
|
315
|
+
const newFields = [...fields];
|
|
316
|
+
const val = parseInt(e, 10);
|
|
317
|
+
if (!val)
|
|
318
|
+
newFields[index].maxlength = undefined;
|
|
319
|
+
else
|
|
320
|
+
newFields[index].maxlength = val;
|
|
321
|
+
setFields(newFields);
|
|
322
|
+
}}
|
|
323
|
+
/>
|
|
324
|
+
</label>
|
|
325
|
+
</div>
|
|
326
|
+
</>
|
|
327
|
+
)}
|
|
328
|
+
{(field.itemsType || field.type) === 'number' && (
|
|
329
|
+
<>
|
|
330
|
+
<div className={"flex flex-no-wrap"}>
|
|
331
|
+
{hint('modelcreator.precision.hint')}
|
|
332
|
+
<label className="flex">
|
|
333
|
+
<span><Trans
|
|
334
|
+
i18nKey={"modelcreator.precision"}>Précision :</Trans></span>
|
|
335
|
+
<div className="flex-1"><NumberField
|
|
336
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
337
|
+
value={field.step || 1}
|
|
338
|
+
step={0.1}
|
|
339
|
+
placeholder={t('modelcreator.field.step.ph', "Précision (1, 0.1...)")}
|
|
340
|
+
onChange={(e) => {
|
|
341
|
+
const newFields = [...fields];
|
|
342
|
+
newFields[index].step = e.target.value;//.replace('.', ','));
|
|
343
|
+
setFields(newFields);
|
|
344
|
+
}}
|
|
345
|
+
/>
|
|
346
|
+
</div>
|
|
347
|
+
</label>
|
|
348
|
+
</div>
|
|
349
|
+
<div className="flex flex-no-wrap">
|
|
350
|
+
{hint('modelcreator.unit.hint')}
|
|
351
|
+
<label className="flex">
|
|
352
|
+
<span><Trans
|
|
353
|
+
i18nKey={"modelcreator.unit"}>Unité :</Trans></span>
|
|
354
|
+
<div className="flex-1"><input
|
|
355
|
+
type="string"
|
|
356
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
357
|
+
value={field.unit}
|
|
358
|
+
placeholder={t('modelcreator.field.unit.ph', "€, cm, kg...")}
|
|
359
|
+
onChange={(e) => {
|
|
360
|
+
const newFields = [...fields];
|
|
361
|
+
newFields[index].unit = e.target.value;
|
|
362
|
+
setFields(newFields);
|
|
363
|
+
}}
|
|
364
|
+
/></div>
|
|
365
|
+
|
|
366
|
+
</label>
|
|
367
|
+
</div>
|
|
368
|
+
</>
|
|
369
|
+
)}
|
|
370
|
+
{(field.itemsType || field.type) === "relation" && (
|
|
371
|
+
<div className="flex flex-no-wrap">
|
|
372
|
+
{hint('modelcreator.multiple.hint')}
|
|
373
|
+
<div className="checkbox-label flex flex-1">
|
|
374
|
+
<CheckboxField
|
|
375
|
+
label={<Trans i18nKey={"modelcreator.multiple"}>Multiple :</Trans>}
|
|
376
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
377
|
+
checked={field.multiple}
|
|
378
|
+
onChange={(e) => {
|
|
379
|
+
const newFields = [...fields];
|
|
380
|
+
newFields[index].multiple = e.target.checked;
|
|
381
|
+
setFields(newFields);
|
|
382
|
+
}}
|
|
383
|
+
/></div>
|
|
384
|
+
</div>)}
|
|
385
|
+
|
|
386
|
+
{(field.itemsType || field.type) === "enum" && (
|
|
387
|
+
<div className="values">
|
|
388
|
+
<p><Trans i18nKey={"modelcreator.enumValues"}>Valeurs possibles
|
|
389
|
+
:</Trans></p>
|
|
390
|
+
<Draggable items={field.items} renderItem={(v, i) => {
|
|
391
|
+
return <div className="flex flex-no-wrap">
|
|
392
|
+
<ValueField v={v}
|
|
393
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
394
|
+
setValue={(value) => {
|
|
395
|
+
const newFields = [...fields];
|
|
396
|
+
newFields[index].items[i] = value;
|
|
397
|
+
setFields(newFields);
|
|
398
|
+
}}/>
|
|
399
|
+
<button type="button"
|
|
400
|
+
onClick={() => handleRemoveValue(index, i)}>
|
|
401
|
+
<FaMinus/>
|
|
402
|
+
</button>
|
|
403
|
+
</div>
|
|
404
|
+
}} onChange={(arr) => {
|
|
405
|
+
const newFields = [...fields];
|
|
406
|
+
newFields[index].items = arr;
|
|
407
|
+
setFields(newFields);
|
|
408
|
+
}}/>
|
|
409
|
+
<button type="button" onClick={() => handleAddValue(index)}><FaPlus/>
|
|
410
|
+
</button>
|
|
411
|
+
</div>
|
|
412
|
+
)}
|
|
413
|
+
|
|
414
|
+
<details className="advanced-options-details">
|
|
415
|
+
<summary>
|
|
416
|
+
<Trans i18nKey={"modelcreator.advancedOptions"}>Advanced options</Trans>
|
|
417
|
+
</summary>
|
|
418
|
+
<>
|
|
419
|
+
|
|
420
|
+
<div className="flex flex-no-wrap">
|
|
421
|
+
{hint('modelcreator.required.hint')}
|
|
422
|
+
<div className="checkbox-label flex flex-1">
|
|
423
|
+
<CheckboxField
|
|
424
|
+
label={<Trans i18nKey={"modelcreator.required"}>Requis :</Trans>}
|
|
425
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
426
|
+
checked={field.required}
|
|
427
|
+
onChange={(e) => {
|
|
428
|
+
const newFields = [...fields];
|
|
429
|
+
newFields[index].required = e.target.checked;
|
|
430
|
+
setFields(newFields);
|
|
431
|
+
}}
|
|
432
|
+
help={field.required && t('modelcreator.required.hint')}
|
|
433
|
+
/>
|
|
434
|
+
</div>
|
|
435
|
+
</div>
|
|
436
|
+
|
|
437
|
+
<div className="flex flex-no-wrap">
|
|
438
|
+
{hint('modelcreator.unique.hint')}
|
|
439
|
+
<div className="checkbox-label flex flex-1">
|
|
440
|
+
|
|
441
|
+
<CheckboxField
|
|
442
|
+
label={<Trans i18nKey={"modelcreator.unique"}>Unique :</Trans>}
|
|
443
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
444
|
+
checked={field.unique}
|
|
445
|
+
onChange={(e) => {
|
|
446
|
+
const newFields = [...fields];
|
|
447
|
+
newFields[index].unique = e.target.checked;
|
|
448
|
+
setFields(newFields);
|
|
449
|
+
}}
|
|
450
|
+
help={field.unique && t('modelcreator.unique.hint')}
|
|
451
|
+
/>
|
|
452
|
+
</div>
|
|
453
|
+
</div>
|
|
454
|
+
|
|
455
|
+
{!['file', 'relation', 'array', 'calculated'].includes(field.type) && (<div
|
|
456
|
+
className="flex flex-no-wrap">
|
|
457
|
+
{hint('modelcreator.default.hint')}
|
|
458
|
+
|
|
459
|
+
{['string_t', 'string', 'richtext', 'password', 'url', 'phone', 'email'].includes(field.type) && (<>
|
|
460
|
+
<label className="flex flex-1">
|
|
461
|
+
<Trans i18nKey={"modelcreator.default"}>Valeur par défaut :</Trans>
|
|
462
|
+
<input
|
|
463
|
+
type="text"
|
|
464
|
+
className="flex-1"
|
|
465
|
+
value={field.default}
|
|
466
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
467
|
+
onChange={(e) => {
|
|
468
|
+
const newFields = [...fields];
|
|
469
|
+
newFields[index].default = e.target.value;
|
|
470
|
+
setFields(newFields);
|
|
471
|
+
}}
|
|
472
|
+
/>
|
|
473
|
+
</label>
|
|
474
|
+
</>)}
|
|
475
|
+
{['number'].includes(field.type) && (<>
|
|
476
|
+
<NumberField
|
|
477
|
+
label={<Trans i18nKey={"modelcreator.default"}>Valeur par défaut :</Trans>}
|
|
478
|
+
type="number"
|
|
479
|
+
className="flex-1"
|
|
480
|
+
value={field.default}
|
|
481
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
482
|
+
onChange={(e) => {
|
|
483
|
+
const newFields = [...fields];
|
|
484
|
+
const val = parseInt(e.target.value, 10);
|
|
485
|
+
if (!val)
|
|
486
|
+
newFields[index].default = undefined;
|
|
487
|
+
else
|
|
488
|
+
newFields[index].default = val;
|
|
489
|
+
setFields(newFields);
|
|
490
|
+
}}
|
|
491
|
+
/>
|
|
492
|
+
</>)}
|
|
493
|
+
{['enum'].includes(field.type) && (<>
|
|
494
|
+
<SelectField
|
|
495
|
+
label={<Trans i18nKey={"modelcreator.default"}>Valeur par défaut :</Trans>}
|
|
496
|
+
value={field.default}
|
|
497
|
+
className="flex-1"
|
|
498
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
499
|
+
items={(field.items || []).map(m => ({label: t(m, m), value: m}))}
|
|
500
|
+
onChange={(e) => {
|
|
501
|
+
const newFields = [...fields];
|
|
502
|
+
newFields[index].default = e.value;
|
|
503
|
+
setFields(newFields);
|
|
504
|
+
}}
|
|
505
|
+
/>
|
|
506
|
+
</>)}
|
|
507
|
+
{['code'].includes(field.type) && (<>
|
|
508
|
+
<CodeField
|
|
509
|
+
label={<Trans i18nKey={"modelcreator.default"}>Valeur par défaut :</Trans>}
|
|
510
|
+
value={field.language === 'json' ? JSON.stringify(field.default, 2, null) : field.default}
|
|
511
|
+
onChange={(e) => {
|
|
512
|
+
const newFields = [...fields];
|
|
513
|
+
newFields[index].default = e.value;
|
|
514
|
+
setFields(newFields);
|
|
515
|
+
}}
|
|
516
|
+
/>
|
|
517
|
+
</>)}
|
|
518
|
+
{['boolean'].includes(field.type) && (<>
|
|
519
|
+
<div className="checkbox-label flex flex-1">
|
|
520
|
+
<CheckboxField
|
|
521
|
+
label={<Trans i18nKey={"modelcreator.default"}>Valeur par défaut :</Trans>}
|
|
522
|
+
checked={field.default}
|
|
523
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
524
|
+
onChange={(e) => {
|
|
525
|
+
const newFields = [...fields];
|
|
526
|
+
newFields[index].default = e.target.checked;
|
|
527
|
+
setFields(newFields);
|
|
528
|
+
}}
|
|
529
|
+
/>
|
|
530
|
+
</div>
|
|
531
|
+
</>)}
|
|
532
|
+
{['color'].includes(field.type) && (<>
|
|
533
|
+
<ColorField
|
|
534
|
+
label={<Trans i18nKey={"modelcreator.default"}>Valeur par défaut :</Trans>}
|
|
535
|
+
value={field.default || null} name={field.name}
|
|
536
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
537
|
+
onChange={(e) => {
|
|
538
|
+
const newFields = [...fields];
|
|
539
|
+
|
|
540
|
+
newFields[index].default = e.value;
|
|
541
|
+
setFields(newFields);
|
|
542
|
+
}}
|
|
543
|
+
/>
|
|
544
|
+
</>)}
|
|
545
|
+
{['date', 'datetime'].includes(field.type) && (<>
|
|
546
|
+
<label className="flex flex-1">
|
|
547
|
+
<Trans i18nKey={"modelcreator.default"}>Valeur par défaut :</Trans>
|
|
548
|
+
<input
|
|
549
|
+
value={field.default}
|
|
550
|
+
className="flex-1"
|
|
551
|
+
type={field.type === 'date' ? 'date' : 'datetime-local'}
|
|
552
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
553
|
+
onChange={(e) => {
|
|
554
|
+
const newFields = [...fields];
|
|
555
|
+
newFields[index].default = e.target.value;
|
|
556
|
+
setFields(newFields);
|
|
557
|
+
}}
|
|
558
|
+
/>
|
|
559
|
+
</label>
|
|
560
|
+
</>)}
|
|
561
|
+
</div>)}
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
{(['number', 'datetime', 'date'].includes(field.itemsType || field.type)) && (
|
|
565
|
+
<><label className="flex">
|
|
566
|
+
{hint('modelcreator.min.hint')}
|
|
567
|
+
<span><Trans
|
|
568
|
+
i18nKey={"modelcreator.min"}>Valeur minimale :</Trans></span>
|
|
569
|
+
<div className="flex flex-1"><input
|
|
570
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
571
|
+
type={field.type === 'datetime' ? 'datetime-local' : field.type}
|
|
572
|
+
value={field.type === "number" ? (field.min + '').replace('.', ',') : field.min}
|
|
573
|
+
onChange={(e) => {
|
|
574
|
+
const newFields = [...fields];
|
|
575
|
+
if (['datetime', 'date'].includes(field.itemsType || field.type)) {
|
|
576
|
+
newFields[index].min = e.target.value;
|
|
577
|
+
} else {
|
|
578
|
+
newFields[index].min = parseInt(e.target.value, 10);
|
|
579
|
+
}
|
|
580
|
+
setFields(newFields);
|
|
581
|
+
}}
|
|
582
|
+
/></div>
|
|
583
|
+
</label>
|
|
584
|
+
<label className="flex">
|
|
585
|
+
{hint('modelcreator.max.hint')}
|
|
586
|
+
<span><Trans i18nKey={"modelcreator.max"}>Valeur maximale :</Trans></span>
|
|
587
|
+
<div className="flex flex-1"><input
|
|
588
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
589
|
+
type={field.type === 'datetime' ? 'datetime-local' : field.type}
|
|
590
|
+
value={field.type === "number" ? (field.max + '').replace('.', ',') : field.max}
|
|
591
|
+
onChange={(e) => {
|
|
592
|
+
const newFields = [...fields];
|
|
593
|
+
if (['datetime', 'date'].includes(field.itemsType || field.type)) {
|
|
594
|
+
newFields[index].max = e.target.value;
|
|
595
|
+
} else {
|
|
596
|
+
newFields[index].max = parseInt(e.target.value, 10);
|
|
597
|
+
}
|
|
598
|
+
setFields(newFields);
|
|
599
|
+
}}
|
|
600
|
+
/></div>
|
|
601
|
+
</label>
|
|
602
|
+
</>
|
|
603
|
+
)}
|
|
604
|
+
<div className="flex flex-no-wrap">
|
|
605
|
+
{hint('modelcreator.condition.hint')}
|
|
606
|
+
|
|
607
|
+
<label className="checkbox-label flex flex-1"><Trans
|
|
608
|
+
i18nKey={"modelcreator.condition"}>Condition</Trans> :
|
|
609
|
+
<input
|
|
610
|
+
type="checkbox"
|
|
611
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
612
|
+
checked={field.condition !== undefined}
|
|
613
|
+
onChange={(e) => {
|
|
614
|
+
const newFields = [...fields];
|
|
615
|
+
if (e.target.checked) {
|
|
616
|
+
newFields[index].condition = null;
|
|
617
|
+
} else {
|
|
618
|
+
delete newFields[index].condition;
|
|
619
|
+
}
|
|
620
|
+
setFields(newFields);
|
|
621
|
+
}}
|
|
622
|
+
/>
|
|
623
|
+
</label>
|
|
624
|
+
</div>
|
|
625
|
+
{field.condition !== undefined && (
|
|
626
|
+
<div className={"condition-details flex flex-start"}>
|
|
627
|
+
|
|
628
|
+
<p>{t('modelcreator.condition.hint')}</p>
|
|
629
|
+
<ConditionBuilder key="test"
|
|
630
|
+
onChange={(newCondition) => {
|
|
631
|
+
const newFields = [...fields];
|
|
632
|
+
newFields[index].condition = newCondition || {};
|
|
633
|
+
setFields(newFields);
|
|
634
|
+
}} initialValue={field.condition || {}}
|
|
635
|
+
model={model} models={models}/>
|
|
636
|
+
|
|
637
|
+
</div>
|
|
638
|
+
)}
|
|
639
|
+
{field.type === 'code' && (<>
|
|
640
|
+
<div className="flex">
|
|
641
|
+
{hint('modelcreator.language.hint')}
|
|
642
|
+
<label className="checkbox-label flex flex-1">
|
|
643
|
+
<Trans i18nKey={"modelcreator.language"}>Language :</Trans>
|
|
644
|
+
<TextField placeholder={"json, javascript..."}
|
|
645
|
+
value={field.language}
|
|
646
|
+
onChange={e => {
|
|
647
|
+
const newFields = [...fields];
|
|
648
|
+
newFields[index].language = e.target.value;
|
|
649
|
+
setFields(newFields);
|
|
650
|
+
}}/>
|
|
651
|
+
</label>
|
|
652
|
+
</div>
|
|
653
|
+
{field.language === 'json' && (<div className="flex flex-no-wrap">
|
|
654
|
+
{hint('modelcreator.conditionBuilder.hint')}
|
|
655
|
+
<label className="checkbox-label flex flex-1">
|
|
656
|
+
<Trans i18nKey={"modelcreator.conditionBuilder"}>Condition Builder
|
|
657
|
+
:</Trans>
|
|
658
|
+
<input
|
|
659
|
+
type="checkbox"
|
|
660
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
661
|
+
checked={field.conditionBuilder}
|
|
662
|
+
onChange={(e) => {
|
|
663
|
+
const newFields = [...fields];
|
|
664
|
+
newFields[index].conditionBuilder = e.target.checked ? true : undefined;
|
|
665
|
+
setFields(newFields);
|
|
666
|
+
}}
|
|
667
|
+
/>
|
|
668
|
+
</label>
|
|
669
|
+
</div>)}
|
|
670
|
+
|
|
671
|
+
</>)}
|
|
672
|
+
|
|
673
|
+
{me.userPlan === 'premium' && (
|
|
674
|
+
<div className="flex flex-no-wrap"
|
|
675
|
+
title={t('index_field_info', 'Améliore les performances sur la recherche mais demande un espace de stockage plus élevé et diminue les performances sur l\'insertion de données.')}>
|
|
676
|
+
{hint('modelcreator.index.hint')}
|
|
677
|
+
<label className="checkbox-label flex flex-1">
|
|
678
|
+
<Trans i18nKey={"indexed_field"}>Champ indexé</Trans> :
|
|
679
|
+
<input
|
|
680
|
+
type="checkbox"
|
|
681
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
682
|
+
checked={!!field.index}
|
|
683
|
+
onChange={(e) => {
|
|
684
|
+
const newFields = [...fields];
|
|
685
|
+
newFields[index].index = e.target.checked;
|
|
686
|
+
setFields(newFields);
|
|
687
|
+
}}
|
|
688
|
+
/>
|
|
689
|
+
</label>
|
|
690
|
+
</div>)}
|
|
691
|
+
|
|
692
|
+
|
|
693
|
+
{mainFieldsTypes.includes(field.itemsType || field.type) && (<div
|
|
694
|
+
className="flex flex-no-wrap"
|
|
695
|
+
title={t("modelcreator.field.asMain", "Une information principale sera affichée dans le titre de l'enregistrement")}>
|
|
696
|
+
{hint('modelcreator.asMain.hint')}
|
|
697
|
+
<label className="checkbox-label flex flex-1">
|
|
698
|
+
|
|
699
|
+
<CheckboxField
|
|
700
|
+
label={<Trans i18nKey={"modelcreator.asMain"}>Information principale :</Trans>}
|
|
701
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
702
|
+
checked={field.asMain}
|
|
703
|
+
onChange={(e) => {
|
|
704
|
+
const newFields = [...fields];
|
|
705
|
+
newFields[index].asMain = e.target.checked;
|
|
706
|
+
setFields(newFields);
|
|
707
|
+
}}
|
|
708
|
+
help={field.asMain && t('modelcreator.asMain.hint')}
|
|
709
|
+
/>
|
|
710
|
+
</label>
|
|
711
|
+
</div>)}
|
|
712
|
+
|
|
713
|
+
<div className="flex flex-row flex-stretch">
|
|
714
|
+
<div className="flex">
|
|
715
|
+
{hint('modelcreator.description.hint')}
|
|
716
|
+
<label htmlFor={`textarea-model-description${field.name}`}><Trans
|
|
717
|
+
i18nKey={"modelcreator.fieldDesc"}>Description du champ :</Trans></label>
|
|
718
|
+
</div>
|
|
719
|
+
<div className="flex flex-1">
|
|
720
|
+
<textarea id={`textarea-model-description${field.name}`}
|
|
721
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
722
|
+
onChange={(e) => {
|
|
723
|
+
const newFields = [...fields];
|
|
724
|
+
newFields[index].hint = e.target.value;
|
|
725
|
+
setFields(newFields);
|
|
726
|
+
}}
|
|
727
|
+
defaultValue={field.hint || ''}/>
|
|
728
|
+
</div>
|
|
729
|
+
</div>
|
|
730
|
+
|
|
731
|
+
|
|
732
|
+
<label className="flex">
|
|
733
|
+
{hint('modelcreator.color.hint')}
|
|
734
|
+
<Trans i18nKey={"field.color"}>Color :</Trans>
|
|
735
|
+
<ColorField
|
|
736
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
737
|
+
value={field.color || '#FFFFFF'}
|
|
738
|
+
onChange={(e) => {
|
|
739
|
+
const newFields = [...fields];
|
|
740
|
+
newFields[index].color = e.value;
|
|
741
|
+
setFields(newFields);
|
|
742
|
+
}}
|
|
743
|
+
/>
|
|
744
|
+
</label>
|
|
745
|
+
|
|
746
|
+
<div className="flex flex-no-wrap">
|
|
747
|
+
{hint('modelcreator.hiddenable.hint')}
|
|
748
|
+
<div className="checkbox-label flex flex-1">
|
|
749
|
+
|
|
750
|
+
<CheckboxField
|
|
751
|
+
label={<Trans i18nKey={"modelcreator.hiddenable"}>Dissimulable :</Trans>}
|
|
752
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
753
|
+
checked={field.hiddenable}
|
|
754
|
+
onChange={(e) => {
|
|
755
|
+
const newFields = [...fields];
|
|
756
|
+
newFields[index].hiddenable = e.target.checked;
|
|
757
|
+
setFields(newFields);
|
|
758
|
+
}}
|
|
759
|
+
help={field.unique && t('modelcreator.hiddenable.hint')}
|
|
760
|
+
/>
|
|
761
|
+
</div>
|
|
762
|
+
</div>
|
|
763
|
+
|
|
764
|
+
<div className={"flex flex-no-wrap"}>
|
|
765
|
+
{hint('modelcreator.anonymized.hint')}
|
|
766
|
+
<label className={"checkbox-label flex flex-1"}>
|
|
767
|
+
<Trans i18nKey={"modelcreator.anonymized"}>Donnée anonymisée :</Trans>
|
|
768
|
+
<input
|
|
769
|
+
type="checkbox"
|
|
770
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
771
|
+
checked={field.anonymized}
|
|
772
|
+
onChange={(e) => {
|
|
773
|
+
const newFields = [...fields];
|
|
774
|
+
newFields[index].anonymized = e.target.checked;
|
|
775
|
+
setFields(newFields);
|
|
776
|
+
}}
|
|
777
|
+
/>
|
|
778
|
+
</label>
|
|
779
|
+
</div>
|
|
780
|
+
|
|
781
|
+
|
|
782
|
+
{false && (<label className="checkbox-label flex">
|
|
783
|
+
<Trans i18nKey={"modelcreator.locked"}>Locked :</Trans>
|
|
784
|
+
<input
|
|
785
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
786
|
+
type={"checkbox"}
|
|
787
|
+
checked={field.locked}
|
|
788
|
+
onChange={(e) => {
|
|
789
|
+
const newFields = [...fields];
|
|
790
|
+
newFields[index].locked = e.target.checked;
|
|
791
|
+
setFields(newFields);
|
|
792
|
+
}}
|
|
793
|
+
/>
|
|
794
|
+
</label>)}
|
|
795
|
+
</>
|
|
796
|
+
</details>
|
|
797
|
+
|
|
798
|
+
<div className="flex actions">
|
|
799
|
+
<Button type="button" onClick={() => handleRemoveField(index)}>
|
|
800
|
+
<FaTrash/>
|
|
801
|
+
</Button>
|
|
802
|
+
</div>
|
|
803
|
+
</div>
|
|
804
|
+
|
|
805
|
+
</div>
|
|
806
|
+
</div>
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
export default ModelCreatorField;
|