data-primals-engine 1.3.4 → 1.4.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.
- package/README.md +72 -2
- package/client/README.md +20 -0
- package/client/package-lock.json +712 -147
- package/client/package.json +38 -37
- package/client/src/App.jsx +9 -10
- package/client/src/App.scss +7 -1
- package/client/src/Dashboard.jsx +349 -208
- package/client/src/DashboardView.jsx +569 -547
- package/client/src/DataEditor.jsx +20 -2
- package/client/src/DataLayout.jsx +24 -12
- package/client/src/DataTable.jsx +807 -760
- package/client/src/DataTable.scss +187 -90
- package/client/src/Field.jsx +1783 -1656
- package/client/src/ModelCreator.jsx +2 -2
- package/client/src/ModelCreatorField.jsx +906 -804
- package/client/src/ModelList.jsx +2 -2
- package/client/src/PackGallery.jsx +391 -290
- package/client/src/PackGallery.scss +231 -210
- package/client/src/constants.js +16 -4
- package/client/src/translations.js +16750 -16392
- package/package.json +14 -11
- package/src/core.js +9 -0
- package/src/defaultModels.js +18 -10
- package/src/email.js +2 -1
- package/src/gameObject.js +1 -1
- package/src/i18n.js +501 -28
- package/src/modules/auth-google/index.js +51 -0
- package/src/modules/auth-microsoft/index.js +82 -0
- package/src/modules/auth-saml/index.js +90 -0
- package/src/modules/data/data.core.js +5 -1
- package/src/modules/data/data.history.js +489 -489
- package/src/modules/data/data.js +86 -12
- package/src/modules/data/data.routes.js +1691 -1663
- package/src/modules/user.js +19 -0
- package/src/modules/workflow.js +2 -12
- package/src/providers.js +110 -1
- package/src/sso.js +194 -0
- package/test/data.integration.test.js +3 -0
- package/test/user.test.js +1 -1
- 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
|
@@ -1,805 +1,907 @@
|
|
|
1
|
-
import React, {useState} from "react";
|
|
2
|
-
import {useModelContext} from "./contexts/ModelContext.jsx";
|
|
3
|
-
import {useAuthContext} from "./contexts/AuthContext.jsx";
|
|
4
|
-
import {isLocalUser} from "../../src/data.js";
|
|
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 "../../src/constants.js";
|
|
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
|
-
return (
|
|
47
|
-
<div className="field-edit">
|
|
48
|
-
|
|
49
|
-
<div className="actions right">
|
|
50
|
-
{index < fields.length - 1 && (
|
|
51
|
-
<button title={"Déplacer vers le bas"} type="button"
|
|
52
|
-
onClick={() => handleDown(index)}>
|
|
53
|
-
<FaArrowDown/>
|
|
54
|
-
</button>)}
|
|
55
|
-
{index > 0 && (
|
|
56
|
-
<button title="Déplacer vers le haut" type="button"
|
|
57
|
-
onClick={() => handleUp(index)}>
|
|
58
|
-
<FaArrowUp/>
|
|
59
|
-
</button>)}
|
|
60
|
-
</div>
|
|
61
|
-
<div className="flex flex-row flex-stretch">
|
|
62
|
-
|
|
63
|
-
<div className="flex fieldName field-bg">{hint('modelcreator.name.hint')}
|
|
64
|
-
<div className="flex flex-no-gap flex-no-wrap flex-1">
|
|
65
|
-
<TextField
|
|
66
|
-
label={t('modelcreator.fieldName')}
|
|
67
|
-
className={!modelLocked && !field._isNewField && "input-fit"}
|
|
68
|
-
disabled={modelLocked || (!!model && !field._isNewField)}
|
|
69
|
-
value={field.name}
|
|
70
|
-
onChange={(e) => {
|
|
71
|
-
const newFields = [...fields];
|
|
72
|
-
newFields[index].name = e.target.value;
|
|
73
|
-
setFields(newFields);
|
|
74
|
-
}}
|
|
75
|
-
help={t('modelcreator.name.hint')}
|
|
76
|
-
required
|
|
77
|
-
after={!(!modelLocked && isLocalUser(me) && field.locked) && !field._isNewField && (
|
|
78
|
-
<Button type={"button"} className={"btn-form btn-last"}
|
|
79
|
-
onClick={() => handleRenameField(index, field.name)}><FaEdit/></Button>)}
|
|
80
|
-
/>
|
|
81
|
-
</div>
|
|
82
|
-
|
|
83
|
-
</div>
|
|
84
|
-
|
|
85
|
-
<div className="flex">
|
|
86
|
-
|
|
87
|
-
<div className="flex flex-1 flex-stretch field-bg flex-no-gap">
|
|
88
|
-
|
|
89
|
-
<SelectField
|
|
90
|
-
label={t('modelcreator.type', 'Type de champ')}
|
|
91
|
-
className={"flex-1"}
|
|
92
|
-
value={field.type}
|
|
93
|
-
onChange={(e) => {
|
|
94
|
-
const newFields = [...fields];
|
|
95
|
-
|
|
96
|
-
const nf = newFields[index];
|
|
97
|
-
nf.type = e.value;
|
|
98
|
-
|
|
99
|
-
setFields(newFields);
|
|
100
|
-
gtag('event', 'model set field ' + e.value);
|
|
101
|
-
}}
|
|
102
|
-
items={[
|
|
103
|
-
{label: t("field.string"), value: "string"},
|
|
104
|
-
{label: t("field.string_t"), value: "string_t"},
|
|
105
|
-
{label: t("field.richtext"), value: "richtext"},
|
|
106
|
-
{label: t("field.richtext_t"), value: "richtext_t"},
|
|
107
|
-
{label: t("field.number"), value: "number"},
|
|
108
|
-
{label: t("field.boolean"), value: "boolean"},
|
|
109
|
-
{label: t("field.enum"), value: "enum"},
|
|
110
|
-
{label: t("field.date"), value: "date"},
|
|
111
|
-
{label: t("field.datetime"), value: "datetime"},
|
|
112
|
-
{label: t("field.password"), value: "password"},
|
|
113
|
-
{label: t("field.email"), value: "email"},
|
|
114
|
-
{label: t("field.phone"), value: "phone"},
|
|
115
|
-
{label: t("field.url"), value: "url"},
|
|
116
|
-
{label: t("field.color"), value: "color"},
|
|
117
|
-
{label: t("field.array"), value: "array"},
|
|
118
|
-
{label: t("field.relation"), value: "relation"},
|
|
119
|
-
{label: t("field.file"), value: "file"},
|
|
120
|
-
{label: t("field.code"), value: "code"},
|
|
121
|
-
{label: t("field.calculated", "calculated data"), value: "calculated"},
|
|
122
|
-
{label: t("field.cronSchedule", "task schedule"), value: "cronSchedule"},
|
|
123
|
-
]}
|
|
124
|
-
/>
|
|
125
|
-
{/* Afficher le sélecteur de modèle lié si le type est "relation" */}
|
|
126
|
-
|
|
127
|
-
{(field.type === 'relation' || field.type === 'array') && <div className={"div"}>
|
|
128
|
-
{/* Afficher le sélecteur de modèle lié si le type est "relation" */}
|
|
129
|
-
{field.type === 'relation' && (
|
|
130
|
-
<>
|
|
131
|
-
<RelationModelSelector
|
|
132
|
-
className={"flex-1 mg-v-1"}
|
|
133
|
-
relation={field.relation}
|
|
134
|
-
onChange={(relation) => {
|
|
135
|
-
const newFields = [...fields];
|
|
136
|
-
newFields[index].relation = relation;
|
|
137
|
-
setFields(newFields);
|
|
138
|
-
|
|
139
|
-
}}
|
|
140
|
-
/>
|
|
141
|
-
</>
|
|
142
|
-
)}
|
|
143
|
-
{/* Afficher le sélecteur de modèle lié si le type est "relation" */}
|
|
144
|
-
{field.type === 'array' && (
|
|
145
|
-
<>
|
|
146
|
-
<select
|
|
147
|
-
className={"flex-1 mg-v-1"}
|
|
148
|
-
value={field.itemsType}
|
|
149
|
-
onChange={(e) => {
|
|
150
|
-
const newFields = [...fields];
|
|
151
|
-
newFields[index].itemsType = e.target.value;
|
|
152
|
-
setFields(newFields);
|
|
153
|
-
|
|
154
|
-
gtag('event', 'model set itemsType ' + e.target.value);
|
|
155
|
-
}}
|
|
156
|
-
>
|
|
157
|
-
<option value="string"><Trans i18nKey={"field.string"}>Texte</Trans></option>
|
|
158
|
-
<option value="string_t"><Trans i18nKey={"field.string_t"}>Texte traduit</Trans>
|
|
159
|
-
</option>
|
|
160
|
-
<option value="richtext"><Trans i18nKey={"field.richtext"}>Texte enrichi</Trans>
|
|
161
|
-
</option>
|
|
162
|
-
<option value="richtext_t"><Trans i18nKey={"field.richtext_t"}>Texte enrichi
|
|
163
|
-
traduit</Trans>
|
|
164
|
-
</option>
|
|
165
|
-
<option value="number"><Trans i18nKey={"field.number"}>Nombre</Trans></option>
|
|
166
|
-
<option value="boolean"><Trans i18nKey={"field.boolean"}>Booléen</Trans>
|
|
167
|
-
</option>
|
|
168
|
-
<option value="date"><Trans i18nKey={"field.date"}>Date</Trans></option>
|
|
169
|
-
<option value="datetime"><Trans i18nKey={"field.datetime"}>Date / Heure</Trans>
|
|
170
|
-
</option>
|
|
171
|
-
<option value="file"><Trans i18nKey={"field.file"}>Fichier</Trans>
|
|
172
|
-
</option>
|
|
173
|
-
<option value="email"><Trans i18nKey={"field.email"}>Email</Trans></option>
|
|
174
|
-
<option value="phone"><Trans i18nKey={"field.phone"}>Téléphone</Trans></option>
|
|
175
|
-
<option value="url"><Trans i18nKey={"field.url"}>URL</Trans></option>
|
|
176
|
-
<option value="color"><Trans i18nKey={"field.color"}>Couleur</Trans></option>
|
|
177
|
-
<option value="code"><Trans i18nKey={"field.code"}>Code</Trans></option>
|
|
178
|
-
<option value="calculated"><Trans i18nKey={"field.calculated"}>Calculated
|
|
179
|
-
data</Trans></option>
|
|
180
|
-
<option value="cronSchedule"><Trans i18nKey={"field.cronSchedule"}>Task
|
|
181
|
-
schedule</Trans></option>
|
|
182
|
-
{/* Ajoutez d'autres types de champs ici */}
|
|
183
|
-
</select>
|
|
184
|
-
</>
|
|
185
|
-
)}
|
|
186
|
-
</div>}
|
|
187
|
-
</div>
|
|
188
|
-
</div>
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
//
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
newFields
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
newFields
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
{
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
newFields
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
{
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
newFields
|
|
287
|
-
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
{
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
)
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
newFields
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
</>)}
|
|
503
|
-
{['
|
|
504
|
-
{hint('modelcreator.default.hint')}
|
|
505
|
-
<
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
<
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
newFields
|
|
577
|
-
|
|
578
|
-
newFields
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
newFields
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
}
|
|
804
|
-
|
|
1
|
+
import React, {useState} from "react";
|
|
2
|
+
import {useModelContext} from "./contexts/ModelContext.jsx";
|
|
3
|
+
import {useAuthContext} from "./contexts/AuthContext.jsx";
|
|
4
|
+
import {isLocalUser} from "../../src/data.js";
|
|
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 "../../src/constants.js";
|
|
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
|
+
return (
|
|
47
|
+
<div className="field-edit">
|
|
48
|
+
|
|
49
|
+
<div className="actions right">
|
|
50
|
+
{index < fields.length - 1 && (
|
|
51
|
+
<button title={"Déplacer vers le bas"} type="button"
|
|
52
|
+
onClick={() => handleDown(index)}>
|
|
53
|
+
<FaArrowDown/>
|
|
54
|
+
</button>)}
|
|
55
|
+
{index > 0 && (
|
|
56
|
+
<button title="Déplacer vers le haut" type="button"
|
|
57
|
+
onClick={() => handleUp(index)}>
|
|
58
|
+
<FaArrowUp/>
|
|
59
|
+
</button>)}
|
|
60
|
+
</div>
|
|
61
|
+
<div className="flex flex-row flex-stretch">
|
|
62
|
+
|
|
63
|
+
<div className="flex fieldName field-bg">{hint('modelcreator.name.hint')}
|
|
64
|
+
<div className="flex flex-no-gap flex-no-wrap flex-1">
|
|
65
|
+
<TextField
|
|
66
|
+
label={t('modelcreator.fieldName')}
|
|
67
|
+
className={!modelLocked && !field._isNewField && "input-fit"}
|
|
68
|
+
disabled={modelLocked || (!!model && !field._isNewField)}
|
|
69
|
+
value={field.name}
|
|
70
|
+
onChange={(e) => {
|
|
71
|
+
const newFields = [...fields];
|
|
72
|
+
newFields[index].name = e.target.value;
|
|
73
|
+
setFields(newFields);
|
|
74
|
+
}}
|
|
75
|
+
help={t('modelcreator.name.hint')}
|
|
76
|
+
required
|
|
77
|
+
after={!(!modelLocked && isLocalUser(me) && field.locked) && !field._isNewField && (
|
|
78
|
+
<Button type={"button"} className={"btn-form btn-last"}
|
|
79
|
+
onClick={() => handleRenameField(index, field.name)}><FaEdit/></Button>)}
|
|
80
|
+
/>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<div className="flex">
|
|
86
|
+
|
|
87
|
+
<div className="flex flex-1 flex-stretch field-bg flex-no-gap">
|
|
88
|
+
|
|
89
|
+
<SelectField
|
|
90
|
+
label={<div className={"flex"}>{hint('modelcreator.type.hint')}{t('modelcreator.type', 'Type de champ')}</div>}
|
|
91
|
+
className={"flex-1"}
|
|
92
|
+
value={field.type}
|
|
93
|
+
onChange={(e) => {
|
|
94
|
+
const newFields = [...fields];
|
|
95
|
+
|
|
96
|
+
const nf = newFields[index];
|
|
97
|
+
nf.type = e.value;
|
|
98
|
+
|
|
99
|
+
setFields(newFields);
|
|
100
|
+
gtag('event', 'model set field ' + e.value);
|
|
101
|
+
}}
|
|
102
|
+
items={[
|
|
103
|
+
{label: t("field.string"), value: "string"},
|
|
104
|
+
{label: t("field.string_t"), value: "string_t"},
|
|
105
|
+
{label: t("field.richtext"), value: "richtext"},
|
|
106
|
+
{label: t("field.richtext_t"), value: "richtext_t"},
|
|
107
|
+
{label: t("field.number"), value: "number"},
|
|
108
|
+
{label: t("field.boolean"), value: "boolean"},
|
|
109
|
+
{label: t("field.enum"), value: "enum"},
|
|
110
|
+
{label: t("field.date"), value: "date"},
|
|
111
|
+
{label: t("field.datetime"), value: "datetime"},
|
|
112
|
+
{label: t("field.password"), value: "password"},
|
|
113
|
+
{label: t("field.email"), value: "email"},
|
|
114
|
+
{label: t("field.phone"), value: "phone"},
|
|
115
|
+
{label: t("field.url"), value: "url"},
|
|
116
|
+
{label: t("field.color"), value: "color"},
|
|
117
|
+
{label: t("field.array"), value: "array"},
|
|
118
|
+
{label: t("field.relation"), value: "relation"},
|
|
119
|
+
{label: t("field.file"), value: "file"},
|
|
120
|
+
{label: t("field.code"), value: "code"},
|
|
121
|
+
{label: t("field.calculated", "calculated data"), value: "calculated"},
|
|
122
|
+
{label: t("field.cronSchedule", "task schedule"), value: "cronSchedule"},
|
|
123
|
+
]}
|
|
124
|
+
/>
|
|
125
|
+
{/* Afficher le sélecteur de modèle lié si le type est "relation" */}
|
|
126
|
+
|
|
127
|
+
{(field.type === 'relation' || field.type === 'array') && <div className={"div"}>
|
|
128
|
+
{/* Afficher le sélecteur de modèle lié si le type est "relation" */}
|
|
129
|
+
{field.type === 'relation' && (
|
|
130
|
+
<>
|
|
131
|
+
<RelationModelSelector
|
|
132
|
+
className={"flex-1 mg-v-1"}
|
|
133
|
+
relation={field.relation}
|
|
134
|
+
onChange={(relation) => {
|
|
135
|
+
const newFields = [...fields];
|
|
136
|
+
newFields[index].relation = relation;
|
|
137
|
+
setFields(newFields);
|
|
138
|
+
|
|
139
|
+
}}
|
|
140
|
+
/>
|
|
141
|
+
</>
|
|
142
|
+
)}
|
|
143
|
+
{/* Afficher le sélecteur de modèle lié si le type est "relation" */}
|
|
144
|
+
{field.type === 'array' && (
|
|
145
|
+
<>
|
|
146
|
+
<select
|
|
147
|
+
className={"flex-1 mg-v-1"}
|
|
148
|
+
value={field.itemsType}
|
|
149
|
+
onChange={(e) => {
|
|
150
|
+
const newFields = [...fields];
|
|
151
|
+
newFields[index].itemsType = e.target.value;
|
|
152
|
+
setFields(newFields);
|
|
153
|
+
|
|
154
|
+
gtag('event', 'model set itemsType ' + e.target.value);
|
|
155
|
+
}}
|
|
156
|
+
>
|
|
157
|
+
<option value="string"><Trans i18nKey={"field.string"}>Texte</Trans></option>
|
|
158
|
+
<option value="string_t"><Trans i18nKey={"field.string_t"}>Texte traduit</Trans>
|
|
159
|
+
</option>
|
|
160
|
+
<option value="richtext"><Trans i18nKey={"field.richtext"}>Texte enrichi</Trans>
|
|
161
|
+
</option>
|
|
162
|
+
<option value="richtext_t"><Trans i18nKey={"field.richtext_t"}>Texte enrichi
|
|
163
|
+
traduit</Trans>
|
|
164
|
+
</option>
|
|
165
|
+
<option value="number"><Trans i18nKey={"field.number"}>Nombre</Trans></option>
|
|
166
|
+
<option value="boolean"><Trans i18nKey={"field.boolean"}>Booléen</Trans>
|
|
167
|
+
</option>
|
|
168
|
+
<option value="date"><Trans i18nKey={"field.date"}>Date</Trans></option>
|
|
169
|
+
<option value="datetime"><Trans i18nKey={"field.datetime"}>Date / Heure</Trans>
|
|
170
|
+
</option>
|
|
171
|
+
<option value="file"><Trans i18nKey={"field.file"}>Fichier</Trans>
|
|
172
|
+
</option>
|
|
173
|
+
<option value="email"><Trans i18nKey={"field.email"}>Email</Trans></option>
|
|
174
|
+
<option value="phone"><Trans i18nKey={"field.phone"}>Téléphone</Trans></option>
|
|
175
|
+
<option value="url"><Trans i18nKey={"field.url"}>URL</Trans></option>
|
|
176
|
+
<option value="color"><Trans i18nKey={"field.color"}>Couleur</Trans></option>
|
|
177
|
+
<option value="code"><Trans i18nKey={"field.code"}>Code</Trans></option>
|
|
178
|
+
<option value="calculated"><Trans i18nKey={"field.calculated"}>Calculated
|
|
179
|
+
data</Trans></option>
|
|
180
|
+
<option value="cronSchedule"><Trans i18nKey={"field.cronSchedule"}>Task
|
|
181
|
+
schedule</Trans></option>
|
|
182
|
+
{/* Ajoutez d'autres types de champs ici */}
|
|
183
|
+
</select>
|
|
184
|
+
</>
|
|
185
|
+
)}
|
|
186
|
+
</div>}
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
|
|
190
|
+
{field.type === 'calculated' && (
|
|
191
|
+
<>
|
|
192
|
+
<CalculationBuilder
|
|
193
|
+
// Clé : Assurez-vous que modelName est utilisé si model n'existe pas (nouveau modèle)
|
|
194
|
+
key={`${model?.name || modelName}-${field.name}-calc`}
|
|
195
|
+
// Prop AJOUTÉE : Nom du modèle en cours de création/modification
|
|
196
|
+
currentModelName={model?.name}
|
|
197
|
+
// Champs disponibles du modèle actuel (on filtre le champ calculé lui-même)
|
|
198
|
+
availableFields={fields.filter(f => f.name !== field.name)}
|
|
199
|
+
// Tous les modèles (pour les relations)
|
|
200
|
+
models={models} // Vient de useModelContext(), semble correct
|
|
201
|
+
initialSteps={field.calculation?.steps || []}
|
|
202
|
+
onCalculationChange={(calc) => {
|
|
203
|
+
const newFields = [...fields];
|
|
204
|
+
newFields[index].calculation = calc; // 'index' est l'index du champ calculé dans 'fields'
|
|
205
|
+
setFields(newFields);
|
|
206
|
+
}}
|
|
207
|
+
/>
|
|
208
|
+
</>
|
|
209
|
+
)}
|
|
210
|
+
{field.type === "relation" && (
|
|
211
|
+
|
|
212
|
+
<div className="flex flex-no-wrap">
|
|
213
|
+
{hint('modelcreator.relationFilter.hint')}
|
|
214
|
+
<label className="checkbox-label flex flex-1"><Trans
|
|
215
|
+
i18nKey={"modelcreator.relationFilter"}>Filtre</Trans> :
|
|
216
|
+
<input
|
|
217
|
+
type="checkbox"
|
|
218
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
219
|
+
checked={field.relationFilter !== undefined}
|
|
220
|
+
onChange={(e) => {
|
|
221
|
+
const newFields = [...fields];
|
|
222
|
+
if (e.target.checked) {
|
|
223
|
+
newFields[index].relationFilter = null;
|
|
224
|
+
} else {
|
|
225
|
+
delete newFields[index].relationFilter;
|
|
226
|
+
}
|
|
227
|
+
setFields(newFields);
|
|
228
|
+
}}
|
|
229
|
+
/>
|
|
230
|
+
</label>
|
|
231
|
+
</div>)}
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
{field.relationFilter !== undefined && Array.isArray(models) && (
|
|
235
|
+
<div className={"condition-details flex flex-start"}>
|
|
236
|
+
<ConditionBuilder
|
|
237
|
+
modelFields={models.find(f => f.name === field.relation)?.fields || []}
|
|
238
|
+
model={field.relation}
|
|
239
|
+
models={models}
|
|
240
|
+
selectableModels={false}
|
|
241
|
+
initialValue={field.relationFilter}
|
|
242
|
+
onChange={(newCondition) => {
|
|
243
|
+
const newFields = [...fields];
|
|
244
|
+
newFields[index].relationFilter = newCondition || {};
|
|
245
|
+
setFields(newFields);
|
|
246
|
+
}}
|
|
247
|
+
/>
|
|
248
|
+
</div>
|
|
249
|
+
)}
|
|
250
|
+
|
|
251
|
+
{(field.itemsType || field.type) === 'file' && (
|
|
252
|
+
<>
|
|
253
|
+
<div className={"flex"}>
|
|
254
|
+
{hint('modelcreator.mimeTypes.hint')}
|
|
255
|
+
<label className={"flex"}>
|
|
256
|
+
<Trans i18nKey={"modelcreator.mimeTypes"}>Types de fichiers
|
|
257
|
+
acceptés
|
|
258
|
+
:</Trans>
|
|
259
|
+
<input
|
|
260
|
+
type="text"
|
|
261
|
+
value={field.mimeTypes}
|
|
262
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
263
|
+
placeholder={t('modelcreator.field.mimeTypes.ph', "image/png, image/jpeg...")}
|
|
264
|
+
onChange={(e) => {
|
|
265
|
+
const newFields = [...fields];
|
|
266
|
+
newFields[index].mimeTypes = e.target.value.split(',').map(m => m.trim());
|
|
267
|
+
setFields(newFields);
|
|
268
|
+
}}
|
|
269
|
+
/>
|
|
270
|
+
</label></div>
|
|
271
|
+
</>
|
|
272
|
+
)}
|
|
273
|
+
|
|
274
|
+
{['string', 'string_t'].includes(field.itemsType || field.type) && (
|
|
275
|
+
<>
|
|
276
|
+
<div className="flex flex-no-wrap">
|
|
277
|
+
{hint('modelcreator.multiline.hint')}
|
|
278
|
+
<div className={"checkbox-label flex flex-1"}>
|
|
279
|
+
<CheckboxField
|
|
280
|
+
label={<Trans i18nKey={"modelcreator.multiline"}>Multi-lignes :</Trans>}
|
|
281
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
282
|
+
checked={field.multiline}
|
|
283
|
+
onChange={(e) => {
|
|
284
|
+
const newFields = [...fields];
|
|
285
|
+
newFields[index].multiline = e;
|
|
286
|
+
setFields(newFields);
|
|
287
|
+
}}
|
|
288
|
+
help={field.multiline && t('modelcreator.multiline.hint')}
|
|
289
|
+
/>
|
|
290
|
+
</div>
|
|
291
|
+
</div>
|
|
292
|
+
</>
|
|
293
|
+
)}
|
|
294
|
+
|
|
295
|
+
{['string', 'string_t', 'richtext', 'richtext_t', 'email', 'phone', 'url', 'password', 'code'].includes(field.itemsType || field.type) && (
|
|
296
|
+
<>
|
|
297
|
+
<div className={"flex flex-no-wrap"}>
|
|
298
|
+
{hint('modelcreator.maxlength.hint')}
|
|
299
|
+
<label className={"flex flex-1"}>
|
|
300
|
+
<Trans i18nKey={"modelcreator.maxlength"}>Longueur
|
|
301
|
+
maximale :</Trans>
|
|
302
|
+
<NumberField
|
|
303
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
304
|
+
step={1}
|
|
305
|
+
min={0}
|
|
306
|
+
className={"flex-1"}
|
|
307
|
+
value={field.maxlength}
|
|
308
|
+
onChange={(e) => {
|
|
309
|
+
const newFields = [...fields];
|
|
310
|
+
const val = parseInt(e, 10);
|
|
311
|
+
if (!val)
|
|
312
|
+
newFields[index].maxlength = undefined;
|
|
313
|
+
else
|
|
314
|
+
newFields[index].maxlength = val;
|
|
315
|
+
setFields(newFields);
|
|
316
|
+
}}
|
|
317
|
+
/>
|
|
318
|
+
</label>
|
|
319
|
+
</div>
|
|
320
|
+
</>
|
|
321
|
+
)}
|
|
322
|
+
{(field.itemsType || field.type) === 'number' && (
|
|
323
|
+
<>
|
|
324
|
+
<div className={"flex flex-no-wrap field-bg"}>
|
|
325
|
+
{hint('modelcreator.precision.hint')}
|
|
326
|
+
<div className="flex-1"><NumberField
|
|
327
|
+
label={<Trans
|
|
328
|
+
i18nKey={"modelcreator.precision"}>Précision :</Trans>}
|
|
329
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
330
|
+
value={field.step || 1}
|
|
331
|
+
step={0.1}
|
|
332
|
+
className={"flex-1"}
|
|
333
|
+
placeholder={t('modelcreator.field.step.ph', "Précision (1, 0.1...)")}
|
|
334
|
+
onChange={(e) => {
|
|
335
|
+
const newFields = [...fields];
|
|
336
|
+
newFields[index].step = e.target.value;//.replace('.', ','));
|
|
337
|
+
setFields(newFields);
|
|
338
|
+
}}
|
|
339
|
+
/>
|
|
340
|
+
</div>
|
|
341
|
+
</div>
|
|
342
|
+
<div className="flex flex-no-wrap field-bg">
|
|
343
|
+
{hint('modelcreator.unit.hint')}
|
|
344
|
+
<div className="flex-1"><TextField
|
|
345
|
+
label={<Trans
|
|
346
|
+
i18nKey={"modelcreator.unit"}>Unité :</Trans>}
|
|
347
|
+
type="string"
|
|
348
|
+
className={"flex-1"}
|
|
349
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
350
|
+
value={field.unit}
|
|
351
|
+
placeholder={t('modelcreator.field.unit.ph', "€, cm, kg...")}
|
|
352
|
+
onChange={(e) => {
|
|
353
|
+
const newFields = [...fields];
|
|
354
|
+
newFields[index].unit = e.target.value;
|
|
355
|
+
setFields(newFields);
|
|
356
|
+
}}
|
|
357
|
+
/></div>
|
|
358
|
+
</div>
|
|
359
|
+
<div className="flex flex-no-wrap">
|
|
360
|
+
{hint('modelcreator.delay.hint')}
|
|
361
|
+
<div className="checkbox-label flex flex-1">
|
|
362
|
+
<CheckboxField
|
|
363
|
+
label={<Trans i18nKey={"modelcreator.delay"}>Délai ?</Trans>}
|
|
364
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
365
|
+
checked={field.delay}
|
|
366
|
+
onChange={(e) => {
|
|
367
|
+
const newFields = [...fields];
|
|
368
|
+
newFields[index].delay = e;
|
|
369
|
+
setFields(newFields);
|
|
370
|
+
}}
|
|
371
|
+
/>
|
|
372
|
+
</div>
|
|
373
|
+
</div>
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
</>
|
|
377
|
+
)}
|
|
378
|
+
{(field.itemsType || field.type) === "relation" && (
|
|
379
|
+
<div className="flex flex-no-wrap">
|
|
380
|
+
{hint('modelcreator.multiple.hint')}
|
|
381
|
+
<div className="checkbox-label flex flex-1">
|
|
382
|
+
<CheckboxField
|
|
383
|
+
label={<Trans i18nKey={"modelcreator.multiple"}>Multiple :</Trans>}
|
|
384
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
385
|
+
checked={field.multiple}
|
|
386
|
+
onChange={(e) => {
|
|
387
|
+
const newFields = [...fields];
|
|
388
|
+
newFields[index].multiple = e;
|
|
389
|
+
setFields(newFields);
|
|
390
|
+
}}
|
|
391
|
+
/></div>
|
|
392
|
+
</div>)}
|
|
393
|
+
|
|
394
|
+
{(field.itemsType || field.type) === "enum" && (
|
|
395
|
+
<div className="values">
|
|
396
|
+
<p><Trans i18nKey={"modelcreator.enumValues"}>Valeurs possibles
|
|
397
|
+
:</Trans></p>
|
|
398
|
+
<Draggable items={field.items} renderItem={(v, i) => {
|
|
399
|
+
return <div className="flex flex-no-wrap">
|
|
400
|
+
<ValueField v={v}
|
|
401
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
402
|
+
setValue={(value) => {
|
|
403
|
+
const newFields = [...fields];
|
|
404
|
+
newFields[index].items[i] = value;
|
|
405
|
+
setFields(newFields);
|
|
406
|
+
}}/>
|
|
407
|
+
<button type="button"
|
|
408
|
+
onClick={() => handleRemoveValue(index, i)}>
|
|
409
|
+
<FaMinus/>
|
|
410
|
+
</button>
|
|
411
|
+
</div>
|
|
412
|
+
}} onChange={(arr) => {
|
|
413
|
+
const newFields = [...fields];
|
|
414
|
+
newFields[index].items = arr;
|
|
415
|
+
setFields(newFields);
|
|
416
|
+
}}/>
|
|
417
|
+
<button type="button" onClick={() => handleAddValue(index)}><FaPlus/>
|
|
418
|
+
</button>
|
|
419
|
+
</div>
|
|
420
|
+
)}
|
|
421
|
+
|
|
422
|
+
<details className="advanced-options-details">
|
|
423
|
+
<summary>
|
|
424
|
+
<Trans i18nKey={"modelcreator.advancedOptions"}>Advanced options</Trans>
|
|
425
|
+
</summary>
|
|
426
|
+
<>
|
|
427
|
+
|
|
428
|
+
<div className="flex flex-no-wrap">
|
|
429
|
+
{hint('modelcreator.required.hint')}
|
|
430
|
+
<div className="checkbox-label flex flex-1">
|
|
431
|
+
<CheckboxField
|
|
432
|
+
label={<Trans i18nKey={"modelcreator.required"}>Requis :</Trans>}
|
|
433
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
434
|
+
checked={field.required}
|
|
435
|
+
onChange={(e) => {
|
|
436
|
+
const newFields = [...fields];
|
|
437
|
+
newFields[index].required = e;
|
|
438
|
+
setFields(newFields);
|
|
439
|
+
}}
|
|
440
|
+
help={field.required && t('modelcreator.required.hint')}
|
|
441
|
+
/>
|
|
442
|
+
</div>
|
|
443
|
+
</div>
|
|
444
|
+
|
|
445
|
+
<div className="flex flex-no-wrap">
|
|
446
|
+
{hint('modelcreator.unique.hint')}
|
|
447
|
+
<div className="checkbox-label flex flex-1">
|
|
448
|
+
|
|
449
|
+
<CheckboxField
|
|
450
|
+
label={<Trans i18nKey={"modelcreator.unique"}>Unique :</Trans>}
|
|
451
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
452
|
+
checked={field.unique}
|
|
453
|
+
onChange={(e) => {
|
|
454
|
+
const newFields = [...fields];
|
|
455
|
+
newFields[index].unique = e;
|
|
456
|
+
setFields(newFields);
|
|
457
|
+
}}
|
|
458
|
+
help={field.unique && t('modelcreator.unique.hint')}
|
|
459
|
+
/>
|
|
460
|
+
</div>
|
|
461
|
+
</div>
|
|
462
|
+
|
|
463
|
+
{!['file', 'relation', 'array', 'calculated'].includes(field.type) && (<div
|
|
464
|
+
className="flex flex-no-wrap mg-item">
|
|
465
|
+
|
|
466
|
+
{['string_t', 'string', 'richtext', 'password', 'url', 'phone', 'email'].includes(field.type) && (<>
|
|
467
|
+
{hint('modelcreator.default.hint')}
|
|
468
|
+
<div className="flex flex-1 field-bg">
|
|
469
|
+
<TextField
|
|
470
|
+
className="flex-1"
|
|
471
|
+
value={field.default}
|
|
472
|
+
label={<Trans i18nKey={"modelcreator.default"}>Valeur par défaut :</Trans>}
|
|
473
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
474
|
+
onChange={(e) => {
|
|
475
|
+
const newFields = [...fields];
|
|
476
|
+
newFields[index].default = e.target.value;
|
|
477
|
+
setFields(newFields);
|
|
478
|
+
}}
|
|
479
|
+
/>
|
|
480
|
+
</div>
|
|
481
|
+
</>)}
|
|
482
|
+
{['number'].includes(field.type) && (<>
|
|
483
|
+
{hint('modelcreator.default.hint')}
|
|
484
|
+
<div className="flex flex-1 field-bg">
|
|
485
|
+
<NumberField
|
|
486
|
+
label={<Trans i18nKey={"modelcreator.default"}>Valeur par défaut :</Trans>}
|
|
487
|
+
type="number"
|
|
488
|
+
className="flex-1"
|
|
489
|
+
value={field.default}
|
|
490
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
491
|
+
onChange={(e) => {
|
|
492
|
+
const newFields = [...fields];
|
|
493
|
+
const val = parseInt(e.target.value, 10);
|
|
494
|
+
if (!val)
|
|
495
|
+
newFields[index].default = undefined;
|
|
496
|
+
else
|
|
497
|
+
newFields[index].default = val;
|
|
498
|
+
setFields(newFields);
|
|
499
|
+
}}
|
|
500
|
+
/>
|
|
501
|
+
</div>
|
|
502
|
+
</>)}
|
|
503
|
+
{['enum'].includes(field.type) && (<>
|
|
504
|
+
{hint('modelcreator.default.hint')}
|
|
505
|
+
<div className="flex flex-1 field-bg">
|
|
506
|
+
<SelectField
|
|
507
|
+
label={<Trans i18nKey={"modelcreator.default"}>Valeur par défaut :</Trans>}
|
|
508
|
+
value={field.default}
|
|
509
|
+
className="flex-1"
|
|
510
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
511
|
+
items={(field.items || []).map(m => ({label: t(m, m), value: m}))}
|
|
512
|
+
onChange={(e) => {
|
|
513
|
+
const newFields = [...fields];
|
|
514
|
+
newFields[index].default = e.value;
|
|
515
|
+
setFields(newFields);
|
|
516
|
+
}}
|
|
517
|
+
/></div>
|
|
518
|
+
</>)}
|
|
519
|
+
{['code'].includes(field.type) && (<>
|
|
520
|
+
{hint('modelcreator.default.hint')}
|
|
521
|
+
<div className="flex flex-1 field-bg">
|
|
522
|
+
<CodeField
|
|
523
|
+
label={<Trans i18nKey={"modelcreator.default"}>Valeur par défaut :</Trans>}
|
|
524
|
+
value={field.language === 'json' ? JSON.stringify(field.default, 2, null) : field.default}
|
|
525
|
+
onChange={(e) => {
|
|
526
|
+
const newFields = [...fields];
|
|
527
|
+
newFields[index].default = e.value;
|
|
528
|
+
setFields(newFields);
|
|
529
|
+
}}
|
|
530
|
+
/>
|
|
531
|
+
</div>
|
|
532
|
+
</>)}
|
|
533
|
+
{['boolean'].includes(field.type) && (<>
|
|
534
|
+
{hint('modelcreator.default.hint')}
|
|
535
|
+
<div className="checkbox-label flex flex-1">
|
|
536
|
+
<CheckboxField
|
|
537
|
+
label={<Trans i18nKey={"modelcreator.default"}>Valeur par défaut :</Trans>}
|
|
538
|
+
checked={field.default}
|
|
539
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
540
|
+
onChange={(e) => {
|
|
541
|
+
const newFields = [...fields];
|
|
542
|
+
newFields[index].default = e;
|
|
543
|
+
setFields(newFields);
|
|
544
|
+
}}
|
|
545
|
+
/>
|
|
546
|
+
</div>
|
|
547
|
+
</>)}
|
|
548
|
+
{['color'].includes(field.type) && (<>
|
|
549
|
+
{hint('modelcreator.default.hint')}
|
|
550
|
+
<div className="flex flex-1 field-bg">
|
|
551
|
+
<ColorField
|
|
552
|
+
label={<Trans i18nKey={"modelcreator.default"}>Valeur par défaut :</Trans>}
|
|
553
|
+
value={field.default || null} name={field.name}
|
|
554
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
555
|
+
onChange={(e) => {
|
|
556
|
+
const newFields = [...fields];
|
|
557
|
+
|
|
558
|
+
newFields[index].default = e.value;
|
|
559
|
+
setFields(newFields);
|
|
560
|
+
}}
|
|
561
|
+
/>
|
|
562
|
+
</div>
|
|
563
|
+
</>)}
|
|
564
|
+
{['date', 'datetime'].includes(field.type) && (<>
|
|
565
|
+
{hint('modelcreator.default.hint')}
|
|
566
|
+
|
|
567
|
+
<div className="flex flex-1 field-bg">
|
|
568
|
+
<label className="flex flex-1">
|
|
569
|
+
<span className={"flex-1"}><Trans i18nKey={"modelcreator.default"}>Valeur par défaut :</Trans></span>
|
|
570
|
+
<input
|
|
571
|
+
value={field.default}
|
|
572
|
+
className="flex-1"
|
|
573
|
+
type={field.type === 'date' ? 'date' : 'datetime-local'}
|
|
574
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
575
|
+
onChange={(e) => {
|
|
576
|
+
const newFields = [...fields];
|
|
577
|
+
newFields[index].default = e.target.value;
|
|
578
|
+
setFields(newFields);
|
|
579
|
+
}}
|
|
580
|
+
/>
|
|
581
|
+
</label>
|
|
582
|
+
</div>
|
|
583
|
+
</>)}
|
|
584
|
+
</div>)}
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
{field.type === 'number' && (
|
|
588
|
+
<>
|
|
589
|
+
<div
|
|
590
|
+
className="flex flex-no-wrap mg-item">{hint('modelcreator.min.hint')}
|
|
591
|
+
<div className="flex field-bg flex-1">
|
|
592
|
+
<NumberField
|
|
593
|
+
label={<Trans
|
|
594
|
+
className={"flex-1"}
|
|
595
|
+
i18nKey={"modelcreator.min"}>Valeur minimale :</Trans>}
|
|
596
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
597
|
+
value={(field.min + '').replace('.', ',')}
|
|
598
|
+
onChange={(e) => {
|
|
599
|
+
const newFields = [...fields];
|
|
600
|
+
newFields[index].min = parseInt(e.target.value, 10);
|
|
601
|
+
setFields(newFields);
|
|
602
|
+
}}
|
|
603
|
+
/>
|
|
604
|
+
</div>
|
|
605
|
+
</div>
|
|
606
|
+
<div
|
|
607
|
+
className="flex flex-no-wrap mg-item">{hint('modelcreator.max.hint')}
|
|
608
|
+
<div className="flex field-bg flex-1">
|
|
609
|
+
<NumberField
|
|
610
|
+
label={<Trans
|
|
611
|
+
className={"flex-1"}
|
|
612
|
+
i18nKey={"modelcreator.max"}>Valeur maximale :</Trans>}
|
|
613
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
614
|
+
value={(field.max + '').replace('.', ',')}
|
|
615
|
+
onChange={(e) => {
|
|
616
|
+
const newFields = [...fields];
|
|
617
|
+
newFields[index].max = parseInt(e.target.value, 10);
|
|
618
|
+
setFields(newFields);
|
|
619
|
+
}}
|
|
620
|
+
/>
|
|
621
|
+
</div>
|
|
622
|
+
</div>
|
|
623
|
+
</>
|
|
624
|
+
)}
|
|
625
|
+
|
|
626
|
+
{(['datetime', 'date'].includes(field.itemsType || field.type)) && (
|
|
627
|
+
<>
|
|
628
|
+
<div className="flex flex-no-wrap mg-item">
|
|
629
|
+
{hint('modelcreator.min.hint')}
|
|
630
|
+
<label className={"flex-1"}><Trans i18nKey={"modelcreator.min"}>Valeur minimale :</Trans></label>
|
|
631
|
+
<div className="flex flex-1 field-bg "><input
|
|
632
|
+
className={"flex-1"}
|
|
633
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
634
|
+
type={field.type === 'datetime' ? 'datetime-local' : field.type}
|
|
635
|
+
value={field.type === "number" ? (field.min + '').replace('.', ',') : field.min}
|
|
636
|
+
onChange={(e) => {
|
|
637
|
+
const newFields = [...fields];
|
|
638
|
+
if (['datetime', 'date'].includes(field.itemsType || field.type)) {
|
|
639
|
+
newFields[index].min = e.target.value;
|
|
640
|
+
} else {
|
|
641
|
+
newFields[index].min = parseInt(e.target.value, 10);
|
|
642
|
+
}
|
|
643
|
+
setFields(newFields);
|
|
644
|
+
}}
|
|
645
|
+
/></div>
|
|
646
|
+
</div>
|
|
647
|
+
|
|
648
|
+
<div className="flex flex-no-wrap mg-item">
|
|
649
|
+
{hint('modelcreator.max.hint')}
|
|
650
|
+
<label className={"flex-1"}><Trans i18nKey={"modelcreator.max"}>Valeur maximale :</Trans></label>
|
|
651
|
+
<div className="flex flex-1 field-bg "><input
|
|
652
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
653
|
+
type={field.type === 'datetime' ? 'datetime-local' : field.type}
|
|
654
|
+
value={field.type === "number" ? (field.max + '').replace('.', ',') : field.max}
|
|
655
|
+
onChange={(e) => {
|
|
656
|
+
const newFields = [...fields];
|
|
657
|
+
if (['datetime', 'date'].includes(field.itemsType || field.type)) {
|
|
658
|
+
newFields[index].max = e.target.value;
|
|
659
|
+
} else {
|
|
660
|
+
newFields[index].max = parseInt(e.target.value, 10);
|
|
661
|
+
}
|
|
662
|
+
setFields(newFields);
|
|
663
|
+
}}
|
|
664
|
+
/></div>
|
|
665
|
+
</div>
|
|
666
|
+
</>
|
|
667
|
+
)}
|
|
668
|
+
<div className="flex flex-no-wrap mg-item">
|
|
669
|
+
{hint('modelcreator.condition.hint')}
|
|
670
|
+
|
|
671
|
+
<CheckboxField
|
|
672
|
+
label={<Trans
|
|
673
|
+
i18nKey={"modelcreator.condition"}>Condition</Trans>}
|
|
674
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
675
|
+
checked={field.condition !== undefined}
|
|
676
|
+
onChange={(e) => {
|
|
677
|
+
const newFields = [...fields];
|
|
678
|
+
if (e) {
|
|
679
|
+
newFields[index].condition = null;
|
|
680
|
+
} else {
|
|
681
|
+
delete newFields[index].condition;
|
|
682
|
+
}
|
|
683
|
+
setFields(newFields);
|
|
684
|
+
}}
|
|
685
|
+
/>
|
|
686
|
+
</div>
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
{field.type=== 'number'&&(
|
|
690
|
+
<div className="flex flex-no-wrap">
|
|
691
|
+
{hint('modelcreator.gauge.hint')}
|
|
692
|
+
<div className="checkbox-label flex flex-1">
|
|
693
|
+
<CheckboxField
|
|
694
|
+
label={<Trans i18nKey={"modelcreator.gauge"}>Jauge ?</Trans>}
|
|
695
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
696
|
+
checked={field.gauge}
|
|
697
|
+
onChange={(e) => {
|
|
698
|
+
const newFields = [...fields];
|
|
699
|
+
newFields[index].gauge = e;
|
|
700
|
+
if (!e) {
|
|
701
|
+
delete newFields[index].percent;
|
|
702
|
+
}
|
|
703
|
+
setFields(newFields);
|
|
704
|
+
}}
|
|
705
|
+
/>
|
|
706
|
+
</div>
|
|
707
|
+
{field.gauge && (
|
|
708
|
+
<div className="flex flex-no-wrap">
|
|
709
|
+
{hint('modelcreator.percent.hint')}
|
|
710
|
+
<div className="checkbox-label flex flex-1">
|
|
711
|
+
<CheckboxField
|
|
712
|
+
label={<Trans i18nKey={"modelcreator.percent"}>Mode pourcentage ?</Trans>}
|
|
713
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
714
|
+
checked={field.percent}
|
|
715
|
+
onChange={(e) => {
|
|
716
|
+
const newFields = [...fields];
|
|
717
|
+
newFields[index].percent = e;
|
|
718
|
+
setFields(newFields);
|
|
719
|
+
}}
|
|
720
|
+
/>
|
|
721
|
+
</div>
|
|
722
|
+
</div>)}
|
|
723
|
+
</div>
|
|
724
|
+
)}
|
|
725
|
+
|
|
726
|
+
{field.condition !== undefined && (
|
|
727
|
+
<div className={"condition-details flex flex-start"}>
|
|
728
|
+
|
|
729
|
+
<p>{t('modelcreator.condition.hint')}</p>
|
|
730
|
+
<ConditionBuilder key="test"
|
|
731
|
+
onChange={(newCondition) => {
|
|
732
|
+
const newFields = [...fields];
|
|
733
|
+
newFields[index].condition = newCondition || {};
|
|
734
|
+
setFields(newFields);
|
|
735
|
+
}} initialValue={field.condition || {}}
|
|
736
|
+
model={model} models={models}/>
|
|
737
|
+
|
|
738
|
+
</div>
|
|
739
|
+
)}
|
|
740
|
+
{field.type === 'code' && (<>
|
|
741
|
+
<div className="flex">
|
|
742
|
+
{hint('modelcreator.language.hint')}
|
|
743
|
+
<label className="checkbox-label flex flex-1">
|
|
744
|
+
<Trans i18nKey={"modelcreator.language"}>Language :</Trans>
|
|
745
|
+
<TextField placeholder={"json, javascript..."}
|
|
746
|
+
value={field.language}
|
|
747
|
+
onChange={e => {
|
|
748
|
+
const newFields = [...fields];
|
|
749
|
+
newFields[index].language = e.target.value;
|
|
750
|
+
setFields(newFields);
|
|
751
|
+
}}/>
|
|
752
|
+
</label>
|
|
753
|
+
</div>
|
|
754
|
+
{field.language === 'json' && (<div className="flex flex-no-wrap">
|
|
755
|
+
{hint('modelcreator.conditionBuilder.hint')}
|
|
756
|
+
<label className="checkbox-label flex flex-1">
|
|
757
|
+
<Trans i18nKey={"modelcreator.conditionBuilder"}>Condition Builder
|
|
758
|
+
:</Trans>
|
|
759
|
+
<input
|
|
760
|
+
type="checkbox"
|
|
761
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
762
|
+
checked={field.conditionBuilder}
|
|
763
|
+
onChange={(e) => {
|
|
764
|
+
const newFields = [...fields];
|
|
765
|
+
newFields[index].conditionBuilder = e.target.checked ? true : undefined;
|
|
766
|
+
setFields(newFields);
|
|
767
|
+
}}
|
|
768
|
+
/>
|
|
769
|
+
</label>
|
|
770
|
+
</div>)}
|
|
771
|
+
|
|
772
|
+
</>)}
|
|
773
|
+
|
|
774
|
+
{me.userPlan === 'premium' && (
|
|
775
|
+
<div className="flex flex-no-wrap"
|
|
776
|
+
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.')}>
|
|
777
|
+
{hint('modelcreator.index.hint')}
|
|
778
|
+
<label className="checkbox-label flex flex-1">
|
|
779
|
+
<Trans i18nKey={"indexed_field"}>Champ indexé</Trans> :
|
|
780
|
+
<input
|
|
781
|
+
type="checkbox"
|
|
782
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
783
|
+
checked={!!field.index}
|
|
784
|
+
onChange={(e) => {
|
|
785
|
+
const newFields = [...fields];
|
|
786
|
+
newFields[index].index = e.target.checked;
|
|
787
|
+
setFields(newFields);
|
|
788
|
+
}}
|
|
789
|
+
/>
|
|
790
|
+
</label>
|
|
791
|
+
</div>)}
|
|
792
|
+
|
|
793
|
+
|
|
794
|
+
{mainFieldsTypes.includes(field.itemsType || field.type) && (<div
|
|
795
|
+
className="flex flex-no-wrap mg-item"
|
|
796
|
+
title={t("modelcreator.field.asMain", "Une information principale sera affichée dans le titre de l'enregistrement")}>
|
|
797
|
+
{hint('modelcreator.asMain.hint')}
|
|
798
|
+
<div className="flex flex-1">
|
|
799
|
+
|
|
800
|
+
<CheckboxField
|
|
801
|
+
label={<Trans i18nKey={"modelcreator.asMain"}>Information principale :</Trans>}
|
|
802
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
803
|
+
checked={field.asMain}
|
|
804
|
+
onChange={(e) => {
|
|
805
|
+
const newFields = [...fields];
|
|
806
|
+
newFields[index].asMain = e;
|
|
807
|
+
setFields(newFields);
|
|
808
|
+
}}
|
|
809
|
+
help={field.asMain && t('modelcreator.asMain.hint')}
|
|
810
|
+
/>
|
|
811
|
+
</div>
|
|
812
|
+
</div>)}
|
|
813
|
+
|
|
814
|
+
<div className="flex flex-row flex-stretch">
|
|
815
|
+
<div className="flex">
|
|
816
|
+
{hint('modelcreator.description.hint')}
|
|
817
|
+
<label className={"flex-1 field-bg"} htmlFor={`textarea-model-description${field.name}`}><Trans
|
|
818
|
+
i18nKey={"modelcreator.fieldDesc"}>Description du champ :</Trans></label>
|
|
819
|
+
</div>
|
|
820
|
+
<div className="flex flex-1">
|
|
821
|
+
<textarea id={`textarea-model-description${field.name}`}
|
|
822
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
823
|
+
onChange={(e) => {
|
|
824
|
+
const newFields = [...fields];
|
|
825
|
+
newFields[index].hint = e.target.value;
|
|
826
|
+
setFields(newFields);
|
|
827
|
+
}}
|
|
828
|
+
defaultValue={field.hint || ''}/>
|
|
829
|
+
</div>
|
|
830
|
+
</div>
|
|
831
|
+
|
|
832
|
+
|
|
833
|
+
<label className="flex mg-item ">
|
|
834
|
+
{hint('modelcreator.color.hint')}
|
|
835
|
+
<Trans i18nKey={"field.color"}>Color :</Trans>
|
|
836
|
+
<ColorField
|
|
837
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
838
|
+
value={field.color || '#FFFFFF'}
|
|
839
|
+
onChange={(e) => {
|
|
840
|
+
const newFields = [...fields];
|
|
841
|
+
newFields[index].color = e.value;
|
|
842
|
+
setFields(newFields);
|
|
843
|
+
}}
|
|
844
|
+
/>
|
|
845
|
+
</label>
|
|
846
|
+
|
|
847
|
+
<div className="flex flex-no-wrap">
|
|
848
|
+
{hint('modelcreator.hiddenable.hint')}
|
|
849
|
+
<div className="checkbox-label flex flex-1">
|
|
850
|
+
|
|
851
|
+
<CheckboxField
|
|
852
|
+
label={<Trans i18nKey={"modelcreator.hiddenable"}>Dissimulable :</Trans>}
|
|
853
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
854
|
+
checked={field.hiddenable}
|
|
855
|
+
onChange={(e) => {
|
|
856
|
+
const newFields = [...fields];
|
|
857
|
+
newFields[index].hiddenable = e;
|
|
858
|
+
setFields(newFields);
|
|
859
|
+
}}
|
|
860
|
+
help={field.unique && t('modelcreator.hiddenable.hint')}
|
|
861
|
+
/>
|
|
862
|
+
</div>
|
|
863
|
+
</div>
|
|
864
|
+
|
|
865
|
+
<div className={"flex flex-no-wrap"}>
|
|
866
|
+
{hint('modelcreator.anonymized.hint')}
|
|
867
|
+
<CheckboxField
|
|
868
|
+
label={<Trans i18nKey={"modelcreator.anonymized"}>Donnée anonymisée :</Trans>}
|
|
869
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
870
|
+
checked={field.anonymized}
|
|
871
|
+
onChange={(e) => {
|
|
872
|
+
const newFields = [...fields];
|
|
873
|
+
newFields[index].anonymized = e;
|
|
874
|
+
setFields(newFields);
|
|
875
|
+
}}
|
|
876
|
+
/>
|
|
877
|
+
</div>
|
|
878
|
+
|
|
879
|
+
|
|
880
|
+
{false && (<label className="checkbox-label flex">
|
|
881
|
+
<Trans i18nKey={"modelcreator.locked"}>Locked :</Trans>
|
|
882
|
+
<input
|
|
883
|
+
disabled={modelLocked || (isLocalUser(me) && field.locked)}
|
|
884
|
+
type={"checkbox"}
|
|
885
|
+
checked={field.locked}
|
|
886
|
+
onChange={(e) => {
|
|
887
|
+
const newFields = [...fields];
|
|
888
|
+
newFields[index].locked = e.target.checked;
|
|
889
|
+
setFields(newFields);
|
|
890
|
+
}}
|
|
891
|
+
/>
|
|
892
|
+
</label>)}
|
|
893
|
+
</>
|
|
894
|
+
</details>
|
|
895
|
+
|
|
896
|
+
<div className="flex actions">
|
|
897
|
+
<Button type="button" onClick={() => handleRemoveField(index)}>
|
|
898
|
+
<FaTrash/>
|
|
899
|
+
</Button>
|
|
900
|
+
</div>
|
|
901
|
+
</div>
|
|
902
|
+
|
|
903
|
+
</div>
|
|
904
|
+
);
|
|
905
|
+
}
|
|
906
|
+
|
|
805
907
|
export default ModelCreatorField;
|