pcm-shared-components 2.1.406 → 2.1.408

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.
@@ -22,6 +22,22 @@ const isFullWidthField = field => ['select', 'textarea', 'select_multiple', 'not
22
22
  // through unflattened still works.
23
23
  const readFieldProp = (field, key) => field[key] !== undefined ? field[key] : field.meta?.[key];
24
24
 
25
+ // Same type-aware required-field wording as AnimalVehicleDetailsEditor's tab-per-animal grid
26
+ // (index.jsx, this directory) -- duplicated rather than shared, matching the small amount of
27
+ // tolerated duplication already elsewhere in this feature (e.g. each host app keeps its own copy
28
+ // for its live-preview sandbox). Falls back to animal.required_error for text/textarea. 'notice'
29
+ // gets its own message rather than the generic one -- its required-satisfying condition is
30
+ // stricter than every other type (see isFieldAnswered below): only a CHECKED "I have read this"
31
+ // box counts, so "please fill this in" would read wrong for what's actually a checkbox.
32
+ const REQUIRED_ERROR_KEY_BY_TYPE = {
33
+ select: 'animal.required_error_select',
34
+ select_multiple: 'animal.required_error_select_multiple',
35
+ number: 'animal.required_error_number',
36
+ date: 'animal.required_error_date',
37
+ yes_no: 'animal.required_error_yes_no',
38
+ notice: 'animal.required_error_notice'
39
+ };
40
+
25
41
  /**
26
42
  * Flat, non-tabbed block for a lot's "once per reservation" animal fields: no tabs, no per-animal
27
43
  * chrome, no add/remove -- these are single shared values/notices for the whole reservation, not
@@ -59,12 +75,14 @@ export const AnimalFieldsOnceBlock = props => {
59
75
  fields,
60
76
  values,
61
77
  onChange,
78
+ isFieldAnswered,
62
79
  className,
63
80
  theme,
64
81
  i18next
65
82
  } = props;
66
83
  const compTheme = theme || useTheme();
67
84
  const defaultTheme = createTheme(compTheme);
85
+ const requiredErrorText = fieldType => i18next.t(REQUIRED_ERROR_KEY_BY_TYPE[fieldType] || 'animal.required_error');
68
86
  const relevantFields = fields.filter(field => readFieldProp(field, 'once_per_reservation') === true);
69
87
  if (relevantFields.length === 0) return null;
70
88
  return /*#__PURE__*/React.createElement(ThemeProvider, {
@@ -80,6 +98,14 @@ export const AnimalFieldsOnceBlock = props => {
80
98
  }
81
99
  }, relevantFields.map(field => {
82
100
  const rawValue = values?.[field.key] ?? (field.type === 'select_multiple' ? [] : '');
101
+ // Required-and-not-yet-answered gets the same red label + control error state the
102
+ // per-animal grid already shows (index.jsx, this directory) -- this block previously
103
+ // hardcoded error: false/helperText: '' unconditionally, so a required once-per-
104
+ // reservation field (e.g. a mandatory "I have read this" notice, or a required yes_no
105
+ // asked once) never visibly indicated it was outstanding. isFieldAnswered is supplied
106
+ // by the caller (same prop AnimalVehicleDetailsEditor already requires), since it's
107
+ // app-specific logic this shared package doesn't own.
108
+ const showError = field.required && !isFieldAnswered(field.type, rawValue);
83
109
  const labelText = field.required ? `${field.label} *` : field.label;
84
110
  return /*#__PURE__*/React.createElement("div", {
85
111
  key: field.key,
@@ -88,7 +114,8 @@ export const AnimalFieldsOnceBlock = props => {
88
114
  variant: "body2",
89
115
  sx: {
90
116
  fontWeight: 500,
91
- mb: 0.5
117
+ mb: 0.5,
118
+ color: showError ? 'error.main' : 'text.primary'
92
119
  }
93
120
  }, labelText), renderFieldTypeControl({
94
121
  field,
@@ -98,8 +125,8 @@ export const AnimalFieldsOnceBlock = props => {
98
125
  // consumer's own event wiring calls it directly in a test.
99
126
  onChange: onChange ? newValue => onChange(field.key, newValue) : () => {},
100
127
  disabled: !onChange,
101
- error: false,
102
- helperText: '',
128
+ error: showError,
129
+ helperText: showError ? requiredErrorText(field.type) : '',
103
130
  i18next
104
131
  }));
105
132
  }))));
@@ -138,6 +165,10 @@ AnimalFieldsOnceBlock.propTypes = {
138
165
  /** (fieldKey, value) => void. Omit entirely to render every input control disabled/read-only
139
166
  * (review-screen usage) -- see the component doc above. */
140
167
  onChange: PropTypes.func,
168
+ /** (fieldType, value) => boolean. Same function each host app already passes into
169
+ * AnimalVehicleDetailsEditor's isFieldAnswered prop -- required so a required-but-unanswered
170
+ * once-per-reservation field can show the same red label/error state the per-animal grid does. */
171
+ isFieldAnswered: PropTypes.func.isRequired,
141
172
  /** Classes applied to the component's own wrapper div. */
142
173
  className: PropTypes.string,
143
174
  /** Theme object to use on this component, if none provided then the MUI5 theme provider theme is used. */
@@ -119,6 +119,17 @@ const NoticeFieldControl = ({
119
119
  color: error ? 'error' : 'primary'
120
120
  }),
121
121
  label: i18next.t('animal.read_notice_confirmation')
122
+ // The checkbox icon already goes red via `color` above when unchecked-and-required, but
123
+ // the label text next to it (the actual "I have read this" wording someone needs to read
124
+ // and act on) stayed the default color regardless -- targeting FormControlLabel's own
125
+ // label class is the supported way to color just that text, matching every other field's
126
+ // red label treatment above the control.
127
+ ,
128
+ sx: {
129
+ '& .MuiFormControlLabel-label': {
130
+ color: error ? 'error.main' : 'text.primary'
131
+ }
132
+ }
122
133
  }), helperText && /*#__PURE__*/React.createElement(FormHelperText, {
123
134
  error: error
124
135
  }, helperText));
@@ -92,12 +92,29 @@ export const AnimalVehicleDetailsEditor = props => {
92
92
  canAddMore,
93
93
  maxItems,
94
94
  showHistoricalAnswers,
95
+ bare,
95
96
  className,
96
97
  theme,
97
98
  i18next
98
99
  } = props;
99
100
  const compTheme = theme || useTheme();
100
101
  const defaultTheme = createTheme(compTheme);
102
+
103
+ // `bare`: skip this component's own outer card (background/border/rounding) and render its
104
+ // content as a plain fragment instead -- for a caller that wants to place this editor and
105
+ // AnimalFieldsOnceBlock's once-per-reservation fields inside ONE shared outer card (a Divider
106
+ // between them, not two separate adjacent cards), rather than this component supplying its own
107
+ // competing card around just the per-item half. Off by default so every existing usage keeps
108
+ // its current self-contained card look unchanged.
109
+ const OuterWrapper = bare ? React.Fragment : Paper;
110
+ const outerWrapperProps = bare ? {} : {
111
+ variant: 'outlined',
112
+ sx: {
113
+ p: 1.5,
114
+ borderRadius: 2,
115
+ bgcolor: 'action.hover'
116
+ }
117
+ };
101
118
  const keys = KEYS[itemType] || KEYS.animal;
102
119
  const requiredErrorText = fieldType => i18next.t(REQUIRED_ERROR_KEY_BY_TYPE[fieldType] || 'animal.required_error');
103
120
  const missingRequiredFields = item => fieldConfig.filter(f => f.required).filter(f => !isFieldAnswered(f.type, item.values?.[f.key] ?? (f.type === 'select_multiple' ? [] : '')));
@@ -234,14 +251,7 @@ export const AnimalVehicleDetailsEditor = props => {
234
251
  theme: defaultTheme
235
252
  }, /*#__PURE__*/React.createElement("div", {
236
253
  className: className
237
- }, /*#__PURE__*/React.createElement(Paper, {
238
- variant: "outlined",
239
- sx: {
240
- p: 1.5,
241
- borderRadius: 2,
242
- bgcolor: 'action.hover'
243
- }
244
- }, /*#__PURE__*/React.createElement("div", {
254
+ }, /*#__PURE__*/React.createElement(OuterWrapper, outerWrapperProps, /*#__PURE__*/React.createElement("div", {
245
255
  className: "flex items-center justify-between mb-8"
246
256
  }, /*#__PURE__*/React.createElement("div", {
247
257
  className: "flex items-center",
@@ -605,6 +615,7 @@ AnimalVehicleDetailsEditor.defaultProps = {
605
615
  canAddMore: true,
606
616
  maxItems: 0,
607
617
  showHistoricalAnswers: true,
618
+ bare: false,
608
619
  className: 'w-full',
609
620
  theme: undefined,
610
621
  i18next: i18nextLocal
@@ -654,6 +665,10 @@ AnimalVehicleDetailsEditor.propTypes = {
654
665
  * since-retired answer on an EXISTING reservation is legitimate. A guest-facing booking widget
655
666
  * creating a brand-new reservation has no history to show and should pass false. */
656
667
  showHistoricalAnswers: PropTypes.bool,
668
+ /** Skip this component's own outer card (background/border/rounding) and render a plain
669
+ * fragment instead -- for a caller placing this editor and AnimalFieldsOnceBlock's fields inside
670
+ * one shared outer card. Defaults to false so every existing usage keeps its current look. */
671
+ bare: PropTypes.bool,
657
672
  /** Classes applied to the component's own wrapper div. */
658
673
  className: PropTypes.string,
659
674
  /** Theme object to use on this component, if none provided then the MUI5 theme provider theme is used. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pcm-shared-components",
3
- "version": "2.1.406",
3
+ "version": "2.1.408",
4
4
  "private": false,
5
5
  "main": "dist/index.js",
6
6
  "babel": {