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.
- package/dist/components/Reservation/AnimalVehicleDetailsEditor/index.js +139 -5
- package/dist/components/Reservation/animalFieldDrift.js +83 -0
- package/dist/index.js +2 -1
- package/dist/languages/en/translations.json +17 -2
- package/dist/languages/es/translations.json +17 -2
- package/dist/languages/fr/translations.json +17 -2
- package/package.json +1 -1
|
@@ -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
|
|
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 (
|
|
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:
|
|
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
|
-
|
|
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à",
|