pcm-shared-components 2.1.389 → 2.1.391

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.
@@ -285,234 +285,238 @@ export const AnimalVehicleDetailsEditor = props => {
285
285
  onClick: () => requestRemove(selectedItem)
286
286
  }, /*#__PURE__*/React.createElement(DeleteOutlineIcon, {
287
287
  fontSize: "small"
288
- }))), /*#__PURE__*/React.createElement("div", {
289
- ref: fieldsContainerRef,
290
- className: "grid grid-cols-2",
291
- style: {
292
- columnGap: 12,
293
- rowGap: 16,
294
- gridAutoFlow: 'row dense'
295
- }
296
- }, fieldConfig.map(field => {
297
- const rawValue = selectedItem.values?.[field.key] ?? '';
298
- const revealKey = `${selectedItem.id}_${field.key}`;
299
- const wasDrifted = isFieldDrifted(field, rawValue);
300
- const showAsDrifted = wasDrifted && !revealedDriftKeys.has(revealKey);
301
- // Once a drifted field is revealed, the live control gets an EMPTY value, never
302
- // the mismatched raw one -- a drifted answer is never editable-in-place, only
303
- // replaceable. Not drifted (or already revealed): behaves exactly as before.
304
- const value = showAsDrifted ? rawValue : wasDrifted ? field.type === 'select_multiple' ? [] : '' : rawValue;
305
- const showError = field.required && !isFieldAnswered(field.type, value);
306
- // No "(Optional)" suffix -- required fields already say so via the asterisk, so
307
- // spelling out the alternative on every other field was redundant noise, and the
308
- // extra length was long enough to wrap onto a second line in a narrow column,
309
- // throwing that field's input out of vertical alignment with its paired neighbor.
310
- const labelText = field.required ? `${field.label} *` : field.label;
311
- const wrapperClassName = ['select', 'textarea', 'select_multiple'].includes(field.type) ? 'col-span-full' : undefined;
312
- let control;
313
- if (showAsDrifted) {
314
- // Generic stringify -- this renders whatever type the field used to be, which
315
- // may no longer match field.type at all, so it can't reuse any type-specific
316
- // control (a boolean/array/string all need to display as plain read text here).
317
- const displayText = typeof rawValue === 'boolean' ? rawValue ? i18next.t('common.app.yes') : i18next.t('common.app.no') : Array.isArray(rawValue) ? rawValue.join(', ') : String(rawValue);
318
- control = /*#__PURE__*/React.createElement("div", {
319
- className: "flex flex-col",
320
- style: {
321
- gap: 4
322
- }
323
- }, /*#__PURE__*/React.createElement(Paper, {
324
- variant: "outlined",
325
- className: "flex items-center",
326
- sx: {
327
- gap: 6,
328
- px: 1.5,
329
- py: 1,
330
- bgcolor: 'action.hover'
331
- }
332
- }, /*#__PURE__*/React.createElement(WarningAmberIcon, {
333
- fontSize: "small",
334
- color: "warning"
335
- }), /*#__PURE__*/React.createElement(Typography, {
288
+ }))), (() => {
289
+ const pairableFields = fieldConfig.filter(f => !['select', 'textarea', 'select_multiple'].includes(f.type));
290
+ const orphanField = pairableFields.length % 2 === 1 ? pairableFields[pairableFields.length - 1] : null;
291
+ return /*#__PURE__*/React.createElement("div", {
292
+ ref: fieldsContainerRef,
293
+ className: "grid grid-cols-2",
294
+ style: {
295
+ columnGap: 12,
296
+ rowGap: 16,
297
+ gridAutoFlow: 'row dense'
298
+ }
299
+ }, fieldConfig.map(field => {
300
+ const rawValue = selectedItem.values?.[field.key] ?? '';
301
+ const revealKey = `${selectedItem.id}_${field.key}`;
302
+ const wasDrifted = isFieldDrifted(field, rawValue);
303
+ const showAsDrifted = wasDrifted && !revealedDriftKeys.has(revealKey);
304
+ // Once a drifted field is revealed, the live control gets an EMPTY value, never
305
+ // the mismatched raw one -- a drifted answer is never editable-in-place, only
306
+ // replaceable. Not drifted (or already revealed): behaves exactly as before.
307
+ const value = showAsDrifted ? rawValue : wasDrifted ? field.type === 'select_multiple' ? [] : '' : rawValue;
308
+ const showError = field.required && !isFieldAnswered(field.type, value);
309
+ // No "(Optional)" suffix -- required fields already say so via the asterisk, so
310
+ // spelling out the alternative on every other field was redundant noise, and the
311
+ // extra length was long enough to wrap onto a second line in a narrow column,
312
+ // throwing that field's input out of vertical alignment with its paired neighbor.
313
+ const labelText = field.required ? `${field.label} *` : field.label;
314
+ const wrapperClassName = ['select', 'textarea', 'select_multiple'].includes(field.type) || field === orphanField ? 'col-span-full' : undefined;
315
+ let control;
316
+ if (showAsDrifted) {
317
+ // Generic stringify -- this renders whatever type the field used to be, which
318
+ // may no longer match field.type at all, so it can't reuse any type-specific
319
+ // control (a boolean/array/string all need to display as plain read text here).
320
+ const displayText = typeof rawValue === 'boolean' ? rawValue ? i18next.t('common.app.yes') : i18next.t('common.app.no') : Array.isArray(rawValue) ? rawValue.join(', ') : String(rawValue);
321
+ control = /*#__PURE__*/React.createElement("div", {
322
+ className: "flex flex-col",
323
+ style: {
324
+ gap: 4
325
+ }
326
+ }, /*#__PURE__*/React.createElement(Paper, {
327
+ variant: "outlined",
328
+ className: "flex items-center",
329
+ sx: {
330
+ gap: 6,
331
+ px: 1.5,
332
+ py: 1,
333
+ bgcolor: 'action.hover'
334
+ }
335
+ }, /*#__PURE__*/React.createElement(WarningAmberIcon, {
336
+ fontSize: "small",
337
+ color: "warning"
338
+ }), /*#__PURE__*/React.createElement(Typography, {
339
+ variant: "body2",
340
+ sx: {
341
+ flex: 1,
342
+ wordBreak: 'break-word'
343
+ }
344
+ }, displayText)), /*#__PURE__*/React.createElement("div", {
345
+ className: "flex items-center",
346
+ style: {
347
+ gap: 4
348
+ }
349
+ }, /*#__PURE__*/React.createElement(Typography, {
350
+ variant: "caption",
351
+ color: "text.secondary"
352
+ }, i18next.t('animal.field_drifted_hint')), /*#__PURE__*/React.createElement(Button, {
353
+ size: "small",
354
+ onClick: () => revealDrift(revealKey),
355
+ sx: {
356
+ textTransform: 'none',
357
+ minWidth: 0,
358
+ p: 0
359
+ }
360
+ }, i18next.t('animal.field_drifted_replace'))));
361
+ } else if (field.type === 'number') {
362
+ control = /*#__PURE__*/React.createElement(TextField, {
363
+ name: field.key,
364
+ value: value,
365
+ onChange: e => onFieldChange(selectedItem.id, field.key, e.target.value),
366
+ onKeyDown: e => {
367
+ if (e.key === 'e' || e.key === 'E') e.preventDefault();
368
+ },
369
+ variant: "outlined",
370
+ size: "small",
371
+ error: showError,
372
+ helperText: showError ? requiredErrorText(field.type) : '',
373
+ inputProps: {
374
+ maxLength: field.maxLength || 45
375
+ },
376
+ type: "number",
377
+ placeholder: field.placeholder,
378
+ fullWidth: true,
379
+ InputProps: {
380
+ endAdornment: field.unit ? /*#__PURE__*/React.createElement(InputAdornment, {
381
+ position: "end"
382
+ }, field.unit) : undefined
383
+ }
384
+ });
385
+ } else if (field.type === 'select') {
386
+ control = /*#__PURE__*/React.createElement(TextField, {
387
+ select: true,
388
+ name: field.key,
389
+ value: value,
390
+ onChange: e => onFieldChange(selectedItem.id, field.key, e.target.value),
391
+ variant: "outlined",
392
+ size: "small",
393
+ error: showError,
394
+ helperText: showError ? requiredErrorText(field.type) : '',
395
+ fullWidth: true
396
+ }, /*#__PURE__*/React.createElement(MenuItem, {
397
+ value: ""
398
+ }, /*#__PURE__*/React.createElement("em", null, field.placeholder || i18next.t('common.app.select'))), (field.options || []).map(opt => /*#__PURE__*/React.createElement(MenuItem, {
399
+ key: opt,
400
+ value: opt
401
+ }, opt)));
402
+ } else if (field.type === 'textarea') {
403
+ control = /*#__PURE__*/React.createElement(TextField, {
404
+ name: field.key,
405
+ value: value,
406
+ onChange: e => onFieldChange(selectedItem.id, field.key, e.target.value),
407
+ variant: "outlined",
408
+ size: "small",
409
+ error: showError,
410
+ helperText: showError ? requiredErrorText(field.type) : '',
411
+ inputProps: {
412
+ maxLength: field.maxLength || 45
413
+ },
414
+ placeholder: field.placeholder,
415
+ multiline: true,
416
+ rows: 3,
417
+ fullWidth: true
418
+ });
419
+ } else if (field.type === 'select_multiple') {
420
+ const selectedValues = Array.isArray(value) ? value : [];
421
+ control = /*#__PURE__*/React.createElement(TextField, {
422
+ select: true,
423
+ name: field.key,
424
+ value: selectedValues,
425
+ onChange: e => onFieldChange(selectedItem.id, field.key, e.target.value),
426
+ variant: "outlined",
427
+ size: "small",
428
+ error: showError,
429
+ helperText: showError ? requiredErrorText(field.type) : '',
430
+ fullWidth: true,
431
+ SelectProps: {
432
+ multiple: true,
433
+ renderValue: selected => /*#__PURE__*/React.createElement("div", {
434
+ className: "flex flex-wrap gap-4"
435
+ }, selected.map(opt => /*#__PURE__*/React.createElement(Chip, {
436
+ key: opt,
437
+ label: opt,
438
+ size: "small"
439
+ })))
440
+ }
441
+ }, (field.options || []).map(opt => /*#__PURE__*/React.createElement(MenuItem, {
442
+ key: opt,
443
+ value: opt
444
+ }, /*#__PURE__*/React.createElement(Checkbox, {
445
+ checked: selectedValues.indexOf(opt) > -1
446
+ }), /*#__PURE__*/React.createElement(ListItemText, {
447
+ primary: opt
448
+ }))));
449
+ } else if (field.type === 'yes_no') {
450
+ const toggleValue = value === true ? 'yes' : value === false ? 'no' : null;
451
+ control = /*#__PURE__*/React.createElement(ToggleButtonGroup, {
452
+ value: toggleValue,
453
+ exclusive: true,
454
+ onChange: (_, val) => {
455
+ if (val !== null) onFieldChange(selectedItem.id, field.key, val === 'yes');
456
+ },
457
+ fullWidth: true,
458
+ size: "small",
459
+ color: showError ? 'error' : 'standard'
460
+ }, /*#__PURE__*/React.createElement(ToggleButton, {
461
+ value: "yes"
462
+ }, i18next.t('common.app.yes')), /*#__PURE__*/React.createElement(ToggleButton, {
463
+ value: "no"
464
+ }, i18next.t('common.app.no')));
465
+ } else if (field.type === 'date') {
466
+ // Native date input, not each host app's own local DateTimeField wrapper --
467
+ // this component can't depend on either app's app-local component tree.
468
+ // Value is a plain 'YYYY-MM-DD' string in and out, same convention both apps
469
+ // already use for this field type.
470
+ control = /*#__PURE__*/React.createElement(TextField, {
471
+ name: field.key,
472
+ type: "date",
473
+ value: value,
474
+ onChange: e => onFieldChange(selectedItem.id, field.key, e.target.value),
475
+ variant: "outlined",
476
+ size: "small",
477
+ error: showError,
478
+ helperText: showError ? requiredErrorText(field.type) : '',
479
+ fullWidth: true,
480
+ InputLabelProps: {
481
+ shrink: true
482
+ }
483
+ });
484
+ } else {
485
+ // 'text' (default/unknown type too, for backward compat)
486
+ control = /*#__PURE__*/React.createElement(TextField, {
487
+ name: field.key,
488
+ value: value,
489
+ onChange: e => onFieldChange(selectedItem.id, field.key, e.target.value),
490
+ variant: "outlined",
491
+ size: "small",
492
+ error: showError,
493
+ helperText: showError ? requiredErrorText(field.type) : '',
494
+ inputProps: {
495
+ maxLength: field.maxLength || 45
496
+ },
497
+ type: "text",
498
+ placeholder: field.placeholder,
499
+ fullWidth: true
500
+ });
501
+ }
502
+ return /*#__PURE__*/React.createElement("div", {
503
+ className: wrapperClassName,
504
+ key: revealKey,
505
+ "data-field-invalid": showError ? 'true' : undefined
506
+ }, /*#__PURE__*/React.createElement(Typography, {
336
507
  variant: "body2",
337
508
  sx: {
338
- flex: 1,
339
- wordBreak: 'break-word'
340
- }
341
- }, displayText)), /*#__PURE__*/React.createElement("div", {
342
- className: "flex items-center",
343
- style: {
344
- gap: 4
509
+ fontWeight: 500,
510
+ mb: 0.5,
511
+ color: showError ? 'error.main' : 'text.primary'
345
512
  }
346
- }, /*#__PURE__*/React.createElement(Typography, {
513
+ }, labelText), control, field.type === 'yes_no' && !showAsDrifted && showError && /*#__PURE__*/React.createElement(Typography, {
347
514
  variant: "caption",
348
- color: "text.secondary"
349
- }, i18next.t('animal.field_drifted_hint')), /*#__PURE__*/React.createElement(Button, {
350
- size: "small",
351
- onClick: () => revealDrift(revealKey),
352
- sx: {
353
- textTransform: 'none',
354
- minWidth: 0,
355
- p: 0
356
- }
357
- }, i18next.t('animal.field_drifted_replace'))));
358
- } else if (field.type === 'number') {
359
- control = /*#__PURE__*/React.createElement(TextField, {
360
- name: field.key,
361
- value: value,
362
- onChange: e => onFieldChange(selectedItem.id, field.key, e.target.value),
363
- onKeyDown: e => {
364
- if (e.key === 'e' || e.key === 'E') e.preventDefault();
365
- },
366
- variant: "outlined",
367
- size: "small",
368
- error: showError,
369
- helperText: showError ? requiredErrorText(field.type) : '',
370
- inputProps: {
371
- maxLength: field.maxLength || 45
372
- },
373
- type: "number",
374
- placeholder: field.placeholder,
375
- fullWidth: true,
376
- InputProps: {
377
- endAdornment: field.unit ? /*#__PURE__*/React.createElement(InputAdornment, {
378
- position: "end"
379
- }, field.unit) : undefined
380
- }
381
- });
382
- } else if (field.type === 'select') {
383
- control = /*#__PURE__*/React.createElement(TextField, {
384
- select: true,
385
- name: field.key,
386
- value: value,
387
- onChange: e => onFieldChange(selectedItem.id, field.key, e.target.value),
388
- variant: "outlined",
389
- size: "small",
390
- error: showError,
391
- helperText: showError ? requiredErrorText(field.type) : '',
392
- fullWidth: true
393
- }, /*#__PURE__*/React.createElement(MenuItem, {
394
- value: ""
395
- }, /*#__PURE__*/React.createElement("em", null, field.placeholder || i18next.t('common.app.select'))), (field.options || []).map(opt => /*#__PURE__*/React.createElement(MenuItem, {
396
- key: opt,
397
- value: opt
398
- }, opt)));
399
- } else if (field.type === 'textarea') {
400
- control = /*#__PURE__*/React.createElement(TextField, {
401
- name: field.key,
402
- value: value,
403
- onChange: e => onFieldChange(selectedItem.id, field.key, e.target.value),
404
- variant: "outlined",
405
- size: "small",
406
- error: showError,
407
- helperText: showError ? requiredErrorText(field.type) : '',
408
- inputProps: {
409
- maxLength: field.maxLength || 45
410
- },
411
- placeholder: field.placeholder,
412
- multiline: true,
413
- rows: 3,
414
- fullWidth: true
415
- });
416
- } else if (field.type === 'select_multiple') {
417
- const selectedValues = Array.isArray(value) ? value : [];
418
- control = /*#__PURE__*/React.createElement(TextField, {
419
- select: true,
420
- name: field.key,
421
- value: selectedValues,
422
- onChange: e => onFieldChange(selectedItem.id, field.key, e.target.value),
423
- variant: "outlined",
424
- size: "small",
425
- error: showError,
426
- helperText: showError ? requiredErrorText(field.type) : '',
427
- fullWidth: true,
428
- SelectProps: {
429
- multiple: true,
430
- renderValue: selected => /*#__PURE__*/React.createElement("div", {
431
- className: "flex flex-wrap gap-4"
432
- }, selected.map(opt => /*#__PURE__*/React.createElement(Chip, {
433
- key: opt,
434
- label: opt,
435
- size: "small"
436
- })))
437
- }
438
- }, (field.options || []).map(opt => /*#__PURE__*/React.createElement(MenuItem, {
439
- key: opt,
440
- value: opt
441
- }, /*#__PURE__*/React.createElement(Checkbox, {
442
- checked: selectedValues.indexOf(opt) > -1
443
- }), /*#__PURE__*/React.createElement(ListItemText, {
444
- primary: opt
445
- }))));
446
- } else if (field.type === 'yes_no') {
447
- const toggleValue = value === true ? 'yes' : value === false ? 'no' : null;
448
- control = /*#__PURE__*/React.createElement(ToggleButtonGroup, {
449
- value: toggleValue,
450
- exclusive: true,
451
- onChange: (_, val) => {
452
- if (val !== null) onFieldChange(selectedItem.id, field.key, val === 'yes');
453
- },
454
- fullWidth: true,
455
- size: "small",
456
- color: showError ? 'error' : 'standard'
457
- }, /*#__PURE__*/React.createElement(ToggleButton, {
458
- value: "yes"
459
- }, i18next.t('common.app.yes')), /*#__PURE__*/React.createElement(ToggleButton, {
460
- value: "no"
461
- }, i18next.t('common.app.no')));
462
- } else if (field.type === 'date') {
463
- // Native date input, not each host app's own local DateTimeField wrapper --
464
- // this component can't depend on either app's app-local component tree.
465
- // Value is a plain 'YYYY-MM-DD' string in and out, same convention both apps
466
- // already use for this field type.
467
- control = /*#__PURE__*/React.createElement(TextField, {
468
- name: field.key,
469
- type: "date",
470
- value: value,
471
- onChange: e => onFieldChange(selectedItem.id, field.key, e.target.value),
472
- variant: "outlined",
473
- size: "small",
474
- error: showError,
475
- helperText: showError ? requiredErrorText(field.type) : '',
476
- fullWidth: true,
477
- InputLabelProps: {
478
- shrink: true
479
- }
480
- });
481
- } else {
482
- // 'text' (default/unknown type too, for backward compat)
483
- control = /*#__PURE__*/React.createElement(TextField, {
484
- name: field.key,
485
- value: value,
486
- onChange: e => onFieldChange(selectedItem.id, field.key, e.target.value),
487
- variant: "outlined",
488
- size: "small",
489
- error: showError,
490
- helperText: showError ? requiredErrorText(field.type) : '',
491
- inputProps: {
492
- maxLength: field.maxLength || 45
493
- },
494
- type: "text",
495
- placeholder: field.placeholder,
496
- fullWidth: true
497
- });
498
- }
499
- return /*#__PURE__*/React.createElement("div", {
500
- className: wrapperClassName,
501
- key: revealKey,
502
- "data-field-invalid": showError ? 'true' : undefined
503
- }, /*#__PURE__*/React.createElement(Typography, {
504
- variant: "body2",
505
- sx: {
506
- fontWeight: 500,
507
- mb: 0.5,
508
- color: showError ? 'error.main' : 'text.primary'
509
- }
510
- }, labelText), control, field.type === 'yes_no' && !showAsDrifted && showError && /*#__PURE__*/React.createElement(Typography, {
511
- variant: "caption",
512
- color: "error",
513
- className: "block mt-4"
514
- }, requiredErrorText(field.type)));
515
- })))), showHistoricalAnswers && selectedItem && (() => {
515
+ color: "error",
516
+ className: "block mt-4"
517
+ }, requiredErrorText(field.type)));
518
+ }));
519
+ })())), showHistoricalAnswers && selectedItem && (() => {
516
520
  const liveKeys = new Set(fieldConfig.map(f => f.key));
517
521
  const historicalEntries = Object.keys(selectedItem.values || {}).filter(k => !liveKeys.has(k)).map(k => ({
518
522
  key: k,
@@ -2452,6 +2452,7 @@
2452
2452
  "configure": "Configure Fields",
2453
2453
  "preview_hint": "How guests see it",
2454
2454
  "preview_empty": "Add a field to see it here.",
2455
+ "preview_reset_hint": "Clear your test answers to see how this looks when it first loads.",
2455
2456
  "field_type": "Field Type",
2456
2457
  "field_type_text": "Text",
2457
2458
  "field_type_select": "Dropdown (Select One)",
@@ -2452,6 +2452,7 @@
2452
2452
  "configure": "Configurar campos",
2453
2453
  "preview_hint": "Vista del cliente",
2454
2454
  "preview_empty": "Añade un campo para verlo aquí.",
2455
+ "preview_reset_hint": "Borra tus respuestas de prueba para ver cómo se ve cuando se carga por primera vez.",
2455
2456
  "field_type": "Tipo de campo",
2456
2457
  "field_type_text": "Texto",
2457
2458
  "field_type_select": "Desplegable (Seleccionar uno)",
@@ -2452,6 +2452,7 @@
2452
2452
  "configure": "Configurer les champs",
2453
2453
  "preview_hint": "Vue côté client",
2454
2454
  "preview_empty": "Ajoutez un champ pour le voir ici.",
2455
+ "preview_reset_hint": "Effacez vos réponses de test pour voir comment cela s'affiche au premier chargement.",
2455
2456
  "field_type": "Type de champ",
2456
2457
  "field_type_text": "Texte",
2457
2458
  "field_type_select": "Liste déroulante (Sélectionner un)",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pcm-shared-components",
3
- "version": "2.1.389",
3
+ "version": "2.1.391",
4
4
  "private": false,
5
5
  "main": "dist/index.js",
6
6
  "babel": {