pcm-shared-components 2.1.386 → 2.1.388

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.
@@ -8,6 +8,7 @@ import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
8
8
  import CheckCircleIcon from '@mui/icons-material/CheckCircle';
9
9
  import WarningAmberIcon from '@mui/icons-material/WarningAmber';
10
10
  import * as i18nextLocal from 'i18next';
11
+ import { classifyAnimalFieldValue } from '../animalFieldDrift';
11
12
 
12
13
  // Every visible string this component owns lives under these two pre-existing i18next namespaces
13
14
  // (already shared across both PitchCamp apps via pcm-shared-components' translations.json). Keyed
@@ -78,6 +79,7 @@ export const AnimalVehicleDetailsEditor = props => {
78
79
  items,
79
80
  fieldConfig,
80
81
  isFieldAnswered,
82
+ isFieldDrifted,
81
83
  onFieldChange,
82
84
  onAddItem,
83
85
  onRemoveItem,
@@ -112,6 +114,15 @@ export const AnimalVehicleDetailsEditor = props => {
112
114
  onRemoveItem(pendingRemoveId);
113
115
  setPendingRemoveId(null);
114
116
  };
117
+
118
+ // A drifted field (its stored value no longer matches this field's current type/options -- see
119
+ // isFieldDrifted) renders read-only instead of feeding a mismatched value into a live
120
+ // type-specific control (e.g. never shove a raw string into a date picker). "Replace" reveals
121
+ // the normal control for that one field, EMPTY rather than pre-filled with the drifted value, so
122
+ // the only way the drifted value leaves storage is a deliberate fresh answer overwriting it on
123
+ // save. Keyed per item+field so revealing one animal's drifted field never affects another.
124
+ const [revealedDriftKeys, setRevealedDriftKeys] = useState(() => new Set());
125
+ const revealDrift = revealKey => setRevealedDriftKeys(prev => new Set(prev).add(revealKey));
115
126
  const [selectedIndex, setSelectedIndex] = useState(0);
116
127
  // Growing the list (Add) jumps to the newly-added last item, since that's the one the host just
117
128
  // asked for and the one that needs filling in. Shrinking it (Remove) just clamps the current
@@ -260,12 +271,64 @@ export const AnimalVehicleDetailsEditor = props => {
260
271
  gridAutoFlow: 'row dense'
261
272
  }
262
273
  }, fieldConfig.map(field => {
263
- const value = selectedItem.values?.[field.key] ?? '';
274
+ const rawValue = selectedItem.values?.[field.key] ?? '';
275
+ const revealKey = `${selectedItem.id}_${field.key}`;
276
+ const wasDrifted = isFieldDrifted(field, rawValue);
277
+ const showAsDrifted = wasDrifted && !revealedDriftKeys.has(revealKey);
278
+ // Once a drifted field is revealed, the live control gets an EMPTY value, never
279
+ // the mismatched raw one -- a drifted answer is never editable-in-place, only
280
+ // replaceable. Not drifted (or already revealed): behaves exactly as before.
281
+ const value = showAsDrifted ? rawValue : wasDrifted ? field.type === 'select_multiple' ? [] : '' : rawValue;
264
282
  const showError = field.required && !isFieldAnswered(field.type, value);
265
283
  const labelText = field.required ? `${field.label} *` : `${field.label} (${i18next.t('common.app.optional')})`;
266
284
  const wrapperClassName = ['select', 'textarea', 'select_multiple', 'yes_no'].includes(field.type) ? 'col-span-full' : undefined;
267
285
  let control;
268
- if (field.type === 'number') {
286
+ if (showAsDrifted) {
287
+ // Generic stringify -- this renders whatever type the field used to be, which
288
+ // may no longer match field.type at all, so it can't reuse any type-specific
289
+ // control (a boolean/array/string all need to display as plain read text here).
290
+ const displayText = typeof rawValue === 'boolean' ? rawValue ? i18next.t('common.app.yes') : i18next.t('common.app.no') : Array.isArray(rawValue) ? rawValue.join(', ') : String(rawValue);
291
+ control = /*#__PURE__*/React.createElement("div", {
292
+ className: "flex flex-col",
293
+ style: {
294
+ gap: 4
295
+ }
296
+ }, /*#__PURE__*/React.createElement(Paper, {
297
+ variant: "outlined",
298
+ className: "flex items-center",
299
+ sx: {
300
+ gap: 6,
301
+ px: 1.5,
302
+ py: 1,
303
+ bgcolor: 'action.hover'
304
+ }
305
+ }, /*#__PURE__*/React.createElement(WarningAmberIcon, {
306
+ fontSize: "small",
307
+ color: "warning"
308
+ }), /*#__PURE__*/React.createElement(Typography, {
309
+ variant: "body2",
310
+ sx: {
311
+ flex: 1,
312
+ wordBreak: 'break-word'
313
+ }
314
+ }, displayText)), /*#__PURE__*/React.createElement("div", {
315
+ className: "flex items-center",
316
+ style: {
317
+ gap: 4
318
+ }
319
+ }, /*#__PURE__*/React.createElement(Typography, {
320
+ variant: "caption",
321
+ color: "text.secondary"
322
+ }, i18next.t('animal.field_drifted_hint')), /*#__PURE__*/React.createElement(Button, {
323
+ size: "small",
324
+ onClick: () => revealDrift(revealKey),
325
+ sx: {
326
+ textTransform: 'none',
327
+ minWidth: 0,
328
+ p: 0
329
+ }
330
+ }, i18next.t('animal.field_drifted_replace'))));
331
+ } else if (field.type === 'number') {
269
332
  control = /*#__PURE__*/React.createElement(TextField, {
270
333
  name: field.key,
271
334
  value: value,
@@ -407,7 +470,7 @@ export const AnimalVehicleDetailsEditor = props => {
407
470
  }
408
471
  return /*#__PURE__*/React.createElement("div", {
409
472
  className: wrapperClassName,
410
- key: `${selectedItem.id}_${field.key}`,
473
+ key: revealKey,
411
474
  "data-field-invalid": showError ? 'true' : undefined
412
475
  }, /*#__PURE__*/React.createElement(Typography, {
413
476
  variant: "body2",
@@ -416,12 +479,77 @@ export const AnimalVehicleDetailsEditor = props => {
416
479
  mb: 0.5,
417
480
  color: showError ? 'error.main' : 'text.primary'
418
481
  }
419
- }, labelText), control, field.type === 'yes_no' && showError && /*#__PURE__*/React.createElement(Typography, {
482
+ }, labelText), control, field.type === 'yes_no' && !showAsDrifted && showError && /*#__PURE__*/React.createElement(Typography, {
420
483
  variant: "caption",
421
484
  color: "error",
422
485
  className: "block mt-4"
423
486
  }, requiredErrorText(field.type)));
424
- })))), (() => {
487
+ })))), selectedItem && (() => {
488
+ const liveKeys = new Set(fieldConfig.map(f => f.key));
489
+ const historicalEntries = Object.keys(selectedItem.values || {}).filter(k => !liveKeys.has(k)).map(k => ({
490
+ key: k,
491
+ value: selectedItem.values[k]
492
+ })).filter(entry => classifyAnimalFieldValue(null, entry.value) !== 'unanswered');
493
+ if (historicalEntries.length === 0) return null;
494
+ return /*#__PURE__*/React.createElement(Paper, {
495
+ variant: "outlined",
496
+ sx: {
497
+ mt: 1,
498
+ p: 1.5,
499
+ borderRadius: 2,
500
+ bgcolor: 'action.hover'
501
+ }
502
+ }, /*#__PURE__*/React.createElement("div", {
503
+ className: "flex items-center",
504
+ style: {
505
+ gap: 6,
506
+ marginBottom: 4
507
+ }
508
+ }, /*#__PURE__*/React.createElement(WarningAmberIcon, {
509
+ fontSize: "small",
510
+ color: "warning"
511
+ }), /*#__PURE__*/React.createElement(Typography, {
512
+ sx: {
513
+ fontWeight: 700
514
+ }
515
+ }, i18next.t('animal.historical_answers_title'))), /*#__PURE__*/React.createElement(Typography, {
516
+ variant: "caption",
517
+ color: "text.secondary",
518
+ sx: {
519
+ display: 'block',
520
+ mb: 1.5
521
+ }
522
+ }, i18next.t('animal.historical_answers_hint')), /*#__PURE__*/React.createElement("div", {
523
+ className: "grid grid-cols-2",
524
+ style: {
525
+ columnGap: 12,
526
+ rowGap: 12
527
+ }
528
+ }, historicalEntries.map(({
529
+ key: k,
530
+ value: v
531
+ }) => {
532
+ const displayText = typeof v === 'boolean' ? v ? i18next.t('common.app.yes') : i18next.t('common.app.no') : Array.isArray(v) ? v.join(', ') : String(v);
533
+ // No live config left to carry a real label -- same title-cased-slug fallback the
534
+ // read-only review screens use for a retired key.
535
+ const label = k.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
536
+ return /*#__PURE__*/React.createElement("div", {
537
+ key: k,
538
+ className: "flex flex-col"
539
+ }, /*#__PURE__*/React.createElement(Typography, {
540
+ variant: "body2",
541
+ sx: {
542
+ fontWeight: 500,
543
+ color: 'text.secondary'
544
+ }
545
+ }, label), /*#__PURE__*/React.createElement(Typography, {
546
+ variant: "body2",
547
+ sx: {
548
+ color: 'text.primary'
549
+ }
550
+ }, displayText));
551
+ })));
552
+ })(), (() => {
425
553
  const incompleteCount = items.filter(item => missingRequiredFields(item).length > 0).length;
426
554
  if (incompleteCount === 0) return null;
427
555
  return /*#__PURE__*/React.createElement(Alert, {
@@ -470,6 +598,7 @@ AnimalVehicleDetailsEditor.defaultProps = {
470
598
  onFieldChange: () => {},
471
599
  onAddItem: () => {},
472
600
  onRemoveItem: () => {},
601
+ isFieldDrifted: () => false,
473
602
  canAddMore: true,
474
603
  maxItems: 0,
475
604
  className: 'w-full',
@@ -498,6 +627,11 @@ AnimalVehicleDetailsEditor.propTypes = {
498
627
  /** (type, value) => boolean. Injected rather than owned internally, so each host app's existing
499
628
  * "has this field actually been answered" rule (yes_no's `false` counts, etc.) stays authoritative. */
500
629
  isFieldAnswered: PropTypes.func.isRequired,
630
+ /** (field, rawValue) => boolean. Optional -- defaults to always-false (no drift concept at all,
631
+ * e.g. vehicle fields, which have no admin config to drift). When true for a field/value pair,
632
+ * that field renders read-only with a warning + "replace" action instead of feeding a value that
633
+ * no longer matches the field's current type/options into a live type-specific control. */
634
+ isFieldDrifted: PropTypes.func,
501
635
  /** (itemId, fieldKey, value) => void */
502
636
  onFieldChange: PropTypes.func,
503
637
  /** () => void -- host owns the actual array/count mutation. */
@@ -0,0 +1,83 @@
1
+ // A lot's admin-configurable animal fields (label, type, options, required, unit) can change at
2
+ // any time, but a reservation's field_values were entered under whatever the config looked like
3
+ // at that moment. This classifies one stored (field, rawValue) pair against the field's CURRENT
4
+ // live config, so a caller can tell three states apart instead of collapsing "answered under a
5
+ // since-changed config" into either "answered" (misleading -- the value may no longer make sense
6
+ // under today's type/options) or "unanswered" (wrong -- silently hides real historical data).
7
+ //
8
+ // This is intentionally a superset of the isAnimalFieldAnswered every host app already owns: that
9
+ // function only asks "is this non-empty" (its job is required-field validation), this one also
10
+ // asks "does it still make sense under today's config" (its job is drift detection). Every
11
+ // per-type completeness threshold below matches isAnimalFieldAnswered exactly (including the
12
+ // legacy 'text' 2-char minimum, deliberately not extended to other types -- see the identical rule
13
+ // there), so a value that was always genuinely incomplete still classifies as 'unanswered', not
14
+ // 'drifted' -- drift only fires for content that's substantial but doesn't fit the current type's
15
+ // shape (or, for select/select_multiple, isn't in the current option list -- the one check
16
+ // isAnimalFieldAnswered can't make since it isn't given the field's options). This mirrors -- does
17
+ // not replace -- the same per-type shape rules the PHP validators enforce server-side
18
+ // (validate_reservation_edit.php / validate_reservation_new.php), so a value the backend would now
19
+ // blank on a genuine edit is exactly the value this flags as 'drifted' on display.
20
+ //
21
+ // fieldConfig is `null` when the field has been fully retired (no longer in the lot's live config
22
+ // at all) -- there's no type left to hold a value to, so any real content at all is 'drifted'.
23
+
24
+ // Generic "is there real content here, regardless of shape" check -- used only to tell 'drifted'
25
+ // (wrong shape, but something was clearly entered) apart from 'unanswered' (nothing meaningful was
26
+ // ever entered) once a type-specific branch has already ruled out "answered".
27
+ const hasWrongShapeContent = rawValue => {
28
+ if (typeof rawValue === 'boolean') return true;
29
+ if (Array.isArray(rawValue)) return rawValue.length > 0;
30
+ if (typeof rawValue === 'string') return rawValue.trim().length > 0;
31
+ return rawValue !== null && rawValue !== undefined;
32
+ };
33
+ export const classifyAnimalFieldValue = (fieldConfig, rawValue) => {
34
+ if (!fieldConfig) {
35
+ return hasWrongShapeContent(rawValue) ? 'drifted' : 'unanswered';
36
+ }
37
+ const {
38
+ type,
39
+ options
40
+ } = fieldConfig;
41
+ if (type === 'yes_no') {
42
+ if (typeof rawValue === 'boolean') return 'answered';
43
+ return hasWrongShapeContent(rawValue) ? 'drifted' : 'unanswered';
44
+ }
45
+ if (type === 'select_multiple') {
46
+ if (Array.isArray(rawValue)) {
47
+ if (rawValue.length === 0) return 'unanswered';
48
+ const allowed = Array.isArray(options) ? options : [];
49
+ return rawValue.every(v => allowed.includes(v)) ? 'answered' : 'drifted';
50
+ }
51
+ return hasWrongShapeContent(rawValue) ? 'drifted' : 'unanswered';
52
+ }
53
+ if (type === 'select') {
54
+ if (typeof rawValue === 'string' && rawValue !== '') {
55
+ const allowed = Array.isArray(options) ? options : [];
56
+ return allowed.includes(rawValue) ? 'answered' : 'drifted';
57
+ }
58
+ return hasWrongShapeContent(rawValue) ? 'drifted' : 'unanswered';
59
+ }
60
+ if (type === 'number') {
61
+ if (typeof rawValue === 'string' && rawValue.length >= 1) {
62
+ return !Number.isNaN(Number(rawValue)) ? 'answered' : 'drifted';
63
+ }
64
+ return hasWrongShapeContent(rawValue) ? 'drifted' : 'unanswered';
65
+ }
66
+ if (type === 'date') {
67
+ if (typeof rawValue === 'string' && rawValue.length >= 1) {
68
+ return /^\d{4}-\d{2}-\d{2}$/.test(rawValue) ? 'answered' : 'drifted';
69
+ }
70
+ return hasWrongShapeContent(rawValue) ? 'drifted' : 'unanswered';
71
+ }
72
+ if (type === 'textarea') {
73
+ if (typeof rawValue === 'string' && rawValue.length >= 1) return 'answered';
74
+ return hasWrongShapeContent(rawValue) ? 'drifted' : 'unanswered';
75
+ }
76
+ // Legacy 'text' (and the fallback for a missing/unrecognized type): 2-char minimum, matching
77
+ // isAnimalFieldAnswered exactly -- a single stray keystroke was never a real answer, config
78
+ // drift or not, so it's 'unanswered' rather than 'drifted'.
79
+ if (typeof rawValue === 'string' && rawValue.length >= 2) return 'answered';
80
+ if (typeof rawValue === 'string' && rawValue.length === 1) return 'unanswered';
81
+ return hasWrongShapeContent(rawValue) ? 'drifted' : 'unanswered';
82
+ };
83
+ export default classifyAnimalFieldValue;
package/dist/index.js CHANGED
@@ -37,4 +37,5 @@ import VerticalImageTile from './components/Images/VerticalImageTile';
37
37
  import EventCard from './components/Cards/EventCard';
38
38
  import ImageAssets from './components/Icons/ImageAssets';
39
39
  import AnimalVehicleDetailsEditor from './components/Reservation/AnimalVehicleDetailsEditor';
40
- export { TestButton, CodeHighlight, CustomFab, HelpButton, GenericDialogWindow, PcSharedComponentThemeInheritor, ImageCanvasDraw, PCMScrollbar, TwoColumnDisplay, SimpleTab, HrefLink, GenericAppBar, ReusablePopupMenu, SearchBar, TillButton, ProductItemButton, DropdownButton, translationResources, coreLocals, currencySymbol, DateRangeCalendar, DateCalendar, SimpleLabel, CurrencyTextInput, ImageStack, PlatformPaymentPlans, PlatformPlanTitles, PlatformPlanPrices, LoadingBackdrop, SimpleAccordion, PricingCalendar, FancyDate, HorizontalImageTile, VerticalImageTile, EventCard, ImageAssets, AnimalVehicleDetailsEditor };
40
+ import { classifyAnimalFieldValue } from './components/Reservation/animalFieldDrift';
41
+ export { TestButton, CodeHighlight, CustomFab, HelpButton, GenericDialogWindow, PcSharedComponentThemeInheritor, ImageCanvasDraw, PCMScrollbar, TwoColumnDisplay, SimpleTab, HrefLink, GenericAppBar, ReusablePopupMenu, SearchBar, TillButton, ProductItemButton, DropdownButton, translationResources, coreLocals, currencySymbol, DateRangeCalendar, DateCalendar, SimpleLabel, CurrencyTextInput, ImageStack, PlatformPaymentPlans, PlatformPlanTitles, PlatformPlanPrices, LoadingBackdrop, SimpleAccordion, PricingCalendar, FancyDate, HorizontalImageTile, VerticalImageTile, EventCard, ImageAssets, AnimalVehicleDetailsEditor, classifyAnimalFieldValue };
@@ -36,7 +36,12 @@
36
36
  "complete_required_fields": "Complete {{count}} required field",
37
37
  "complete_required_fields_plural": "Complete {{count}} required fields",
38
38
  "remove_confirm_title": "Remove this animal?",
39
- "remove_confirm_message": "All information entered for this animal will be lost. This can't be undone."
39
+ "remove_confirm_message": "All information entered for this animal will be lost. This can't be undone.",
40
+ "field_drifted_hint": "This answer no longer matches this question's current setup.",
41
+ "field_drifted_replace": "Enter a new answer",
42
+ "field_removed": "This question no longer exists in this lot's setup.",
43
+ "historical_answers_title": "Historical Answers",
44
+ "historical_answers_hint": "These questions are no longer part of this lot's setup and can't be edited."
40
45
  },
41
46
  "app": {
42
47
  "common": {
@@ -2460,7 +2465,17 @@
2460
2465
  "unit_label": "Unit",
2461
2466
  "unit_hint": "Shown after the number, e.g. kg",
2462
2467
  "delete_field_confirm_title": "Delete this field?",
2463
- "delete_field_confirm_text": "\"{{label}}\" will be removed from the list. Answers guests already gave for it on existing reservations aren't deleted, but the field won't be asked again."
2468
+ "delete_field_confirm_text": "\"{{label}}\" will be removed from the list. Answers guests already gave for it on existing reservations aren't deleted, but the field won't be asked again.",
2469
+ "change_type_confirm_title": "Change field type?",
2470
+ "change_type_confirm_text": "{{count}} reservation(s) already have an answer for \"{{label}}\". Changing its type may make that answer display incorrectly. Continue?",
2471
+ "remove_option_confirm_title": "Remove option?",
2472
+ "remove_option_confirm_text": "{{count}} reservation(s) have an answer using an option you're removing from \"{{label}}\". Their answer will no longer match this field's choices. Continue?",
2473
+ "visibility_hint": "Also controls whether this section appears in Online Booking → Online Checkout Options.",
2474
+ "hidden_from_guests_warning": "Guests won't see this until you turn it on.",
2475
+ "online_booking_title": "Animal details for online booking",
2476
+ "manage_fields": "Manage fields",
2477
+ "fields_configured_count_one": "{{count}} field configured",
2478
+ "fields_configured_count_other": "{{count}} fields configured"
2464
2479
  },
2465
2480
  "edit_lot": "Edit Lot",
2466
2481
  "lot_id_exist": "Lot number already exist",
@@ -36,7 +36,12 @@
36
36
  "complete_required_fields": "Complete {{count}} campo requerido",
37
37
  "complete_required_fields_plural": "Complete {{count}} campos requeridos",
38
38
  "remove_confirm_title": "¿Eliminar este animal?",
39
- "remove_confirm_message": "Toda la información ingresada para este animal se perderá. Esto no se puede deshacer."
39
+ "remove_confirm_message": "Toda la información ingresada para este animal se perderá. Esto no se puede deshacer.",
40
+ "field_drifted_hint": "Esta respuesta ya no coincide con la configuración actual de esta pregunta.",
41
+ "field_drifted_replace": "Ingrese una nueva respuesta",
42
+ "field_removed": "Esta pregunta ya no existe en la configuración de este lote.",
43
+ "historical_answers_title": "Respuestas Históricas",
44
+ "historical_answers_hint": "Estas preguntas ya no forman parte de la configuración de este lote y no se pueden editar."
40
45
  },
41
46
  "app": {
42
47
  "common": {
@@ -2460,7 +2465,17 @@
2460
2465
  "unit_label": "Unidad",
2461
2466
  "unit_hint": "Se muestra después del número, ej. kg",
2462
2467
  "delete_field_confirm_title": "¿Eliminar este campo?",
2463
- "delete_field_confirm_text": "\"{{label}}\" será eliminado de la lista. Las respuestas que los huéspedes ya han proporcionado no se eliminarán, pero el campo no se volverá a solicitar."
2468
+ "delete_field_confirm_text": "\"{{label}}\" será eliminado de la lista. Las respuestas que los huéspedes ya han proporcionado no se eliminarán, pero el campo no se volverá a solicitar.",
2469
+ "change_type_confirm_title": "¿Cambiar el tipo de campo?",
2470
+ "change_type_confirm_text": "{{count}} reserva(s) ya tienen una respuesta para \"{{label}}\". Cambiar su tipo puede hacer que esa respuesta se muestre incorrectamente. ¿Continuar?",
2471
+ "remove_option_confirm_title": "¿Eliminar la opción?",
2472
+ "remove_option_confirm_text": "{{count}} reserva(s) tienen una respuesta usando una opción que está eliminando de \"{{label}}\". Su respuesta ya no coincidirá con las opciones de este campo. ¿Continuar?",
2473
+ "visibility_hint": "También controla si esta sección aparece en Reserva en línea → Opciones de pago en línea.",
2474
+ "hidden_from_guests_warning": "Los huéspedes no verán esto hasta que lo actives.",
2475
+ "online_booking_title": "Detalles de los animales para la reserva en línea",
2476
+ "manage_fields": "Administrar campos",
2477
+ "fields_configured_count_one": "{{count}} campo configurado",
2478
+ "fields_configured_count_other": "{{count}} campos configurados"
2464
2479
  },
2465
2480
  "edit_lot": "Editar lote",
2466
2481
  "lot_id_exist": "El número de lote ya existe",
@@ -36,7 +36,12 @@
36
36
  "complete_required_fields": "Complétez {{count}} champ requis",
37
37
  "complete_required_fields_plural": "Complétez {{count}} champs requis",
38
38
  "remove_confirm_title": "Supprimer cet animal ?",
39
- "remove_confirm_message": "Toutes les informations saisies pour cet animal seront perdues. Cela ne peut pas être annulé."
39
+ "remove_confirm_message": "Toutes les informations saisies pour cet animal seront perdues. Cela ne peut pas être annulé.",
40
+ "field_drifted_hint": "Cette réponse ne correspond plus à la configuration actuelle de cette question.",
41
+ "field_drifted_replace": "Entrez une nouvelle réponse",
42
+ "field_removed": "Cette question n'existe plus dans la configuration de ce terrain.",
43
+ "historical_answers_title": "Réponses Historiques",
44
+ "historical_answers_hint": "Ces questions ne font plus partie de la configuration de ce terrain et ne peuvent pas être modifiées."
40
45
  },
41
46
  "app": {
42
47
  "common": {
@@ -2460,7 +2465,17 @@
2460
2465
  "unit_label": "Unité",
2461
2466
  "unit_hint": "Affiché après le nombre, ex. kg",
2462
2467
  "delete_field_confirm_title": "Supprimer ce champ ?",
2463
- "delete_field_confirm_text": "« {{label}} » sera supprimé de la liste. Les réponses que les clients ont déjà fournies ne seront pas supprimées, mais le champ ne sera plus demandé."
2468
+ "delete_field_confirm_text": "« {{label}} » sera supprimé de la liste. Les réponses que les clients ont déjà fournies ne seront pas supprimées, mais le champ ne sera plus demandé.",
2469
+ "change_type_confirm_title": "Changer le type de champ ?",
2470
+ "change_type_confirm_text": "{{count}} réservation(s) ont déjà une réponse pour « {{label}} ». Modifier son type peut faire afficher incorrectement cette réponse. Continuer ?",
2471
+ "remove_option_confirm_title": "Supprimer l'option ?",
2472
+ "remove_option_confirm_text": "{{count}} réservation(s) ont une réponse utilisant une option que vous supprimez de « {{label}} ». Leur réponse ne correspondra plus aux choix de ce champ. Continuer ?",
2473
+ "visibility_hint": "Contrôle également l'affichage de cette section dans Réservation en ligne → Options de paiement en ligne.",
2474
+ "hidden_from_guests_warning": "Les clients ne verront cela que lorsque vous l'activerez.",
2475
+ "online_booking_title": "Détails sur les animaux pour la réservation en ligne",
2476
+ "manage_fields": "Gérer les champs",
2477
+ "fields_configured_count_one": "{{count}} champ configuré",
2478
+ "fields_configured_count_other": "{{count}} champs configurés"
2464
2479
  },
2465
2480
  "edit_lot": "Editer le lot",
2466
2481
  "lot_id_exist": "Le numéro de lot existe déjà",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pcm-shared-components",
3
- "version": "2.1.386",
3
+ "version": "2.1.388",
4
4
  "private": false,
5
5
  "main": "dist/index.js",
6
6
  "babel": {