pcm-shared-components 2.1.405 → 2.1.407

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.
@@ -1,155 +1,65 @@
1
- import React, { useLayoutEffect, useRef, useState } from 'react';
1
+ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { Typography, Paper, Button, Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material';
3
+ import { Typography } from '@mui/material';
4
4
  import { ThemeProvider } from '@mui/system';
5
5
  import { createTheme, useTheme } from '@mui/material/styles';
6
6
  import * as i18nextLocal from 'i18next';
7
7
  import { renderFieldTypeControl } from './fieldTypeControl';
8
8
 
9
- // Collapsed height for a notice block before it needs a "View full ..." link. A handful of lines
10
- // of admin-entered text/markup fits inside this without ever showing the link at all; anything
11
- // longer gets clipped here and offered in full through the dialog below.
12
- const NOTICE_COLLAPSED_MAX_HEIGHT = 160;
13
-
14
- // Field-type controls this block ever renders inline (everything else -- 'notice' -- gets its own
15
- // read-only display, not a live input control).
16
- const INPUT_FIELD_TYPES = ['text', 'select', 'select_multiple', 'number', 'textarea', 'yes_no', 'date'];
17
-
18
9
  // Full-width field types, same rule AnimalVehicleDetailsEditor's per-item grid uses for its
19
- // non-orphan fields (a dropdown/textarea/multi-select reads badly squeezed into a half column).
20
- // This block skips that component's further wrapped-label-promotion pass (labelRefs/wrappedKeys in
21
- // index.jsx) -- once-per-reservation fields are typically a handful at most, so the extra
22
- // measurement machinery needed to keep a large tab-per-animal grid's columns aligned isn't worth
23
- // carrying here too.
24
- const isFullWidthField = field => ['select', 'textarea', 'select_multiple'].includes(field.type);
10
+ // non-orphan fields (a dropdown/textarea/multi-select reads badly squeezed into a half column, and
11
+ // a notice's rich text + checkbox needs the same full row). This block skips that component's
12
+ // further wrapped-label-promotion pass (labelRefs/wrappedKeys in index.jsx) -- once-per-reservation
13
+ // fields are typically a handful at most, so the extra measurement machinery needed to keep a large
14
+ // tab-per-animal grid's columns aligned isn't worth carrying here too.
15
+ const isFullWidthField = field => ['select', 'textarea', 'select_multiple', 'notice'].includes(field.type);
25
16
 
26
- // `once_per_reservation`/`content_html` are read from the field object itself first (the flat,
27
- // "normalized" shape this block's `fields` prop expects -- the same convention
28
- // AnimalVehicleDetailsEditor's `fieldConfig` already uses for label/options/unit/placeholder: the
29
- // caller resolves the raw lot_animal_fields.meta payload into flat props before handing fields to
30
- // either component). A `field.meta.<key>` fallback is also accepted so a caller who instead passes
31
- // the meta object through unflattened still works.
17
+ // `once_per_reservation` is read from the field object itself first (the flat, "normalized" shape
18
+ // this block's `fields` prop expects -- the same convention AnimalVehicleDetailsEditor's
19
+ // `fieldConfig` already uses for label/options/unit/placeholder: the caller resolves the raw
20
+ // lot_animal_fields.meta payload into flat props before handing fields to either component). A
21
+ // `field.meta.<key>` fallback is also accepted so a caller who instead passes the meta object
22
+ // through unflattened still works.
32
23
  const readFieldProp = (field, key) => field[key] !== undefined ? field[key] : field.meta?.[key];
33
24
 
34
- /**
35
- * One read-only "View full {label}" notice block: renders `field`'s sanitized content_html inside
36
- * a bounded-height container, and only if that content actually overflows the bound does it show a
37
- * link that opens the full content in a Dialog. Not exported -- internal to AnimalFieldsOnceBlock,
38
- * which is the only place a `type: 'notice'` field ever gets rendered.
39
- */
40
- const NoticeFieldBlock = ({
41
- field,
42
- i18next
43
- }) => {
44
- const contentRef = useRef(null);
45
- const [overflowing, setOverflowing] = useState(false);
46
- const [dialogOpen, setDialogOpen] = useState(false);
47
- // Already HTML-sanitized server-side before this ever reaches the client -- safe to render as-is.
48
- const contentHtml = readFieldProp(field, 'content_html') || '';
49
-
50
- // Measured after mount/paint (useLayoutEffect, not useEffect) so the "View full ..." link never
51
- // flashes in for a heartbeat on content that turns out to fit -- same reasoning
52
- // AnimalVehicleDetailsEditor's own label-wrap measurement (wrappedKeys, index.jsx) already uses.
53
- // Re-checked on window resize too: the height bound is fixed in CSS, but at a narrower viewport
54
- // the same HTML can wrap onto more lines and start overflowing where it didn't before.
55
- useLayoutEffect(() => {
56
- const checkOverflow = () => {
57
- const el = contentRef.current;
58
- if (!el) return;
59
- setOverflowing(el.scrollHeight > el.clientHeight);
60
- };
61
- checkOverflow();
62
- window.addEventListener('resize', checkOverflow);
63
- return () => window.removeEventListener('resize', checkOverflow);
64
- }, [contentHtml]);
65
- return /*#__PURE__*/React.createElement("div", {
66
- className: "flex flex-col",
67
- style: {
68
- gap: 4
69
- }
70
- }, /*#__PURE__*/React.createElement(Typography, {
71
- variant: "body2",
72
- sx: {
73
- fontWeight: 500
74
- }
75
- }, field.label), /*#__PURE__*/React.createElement(Paper, {
76
- variant: "outlined",
77
- sx: {
78
- p: 1.5,
79
- borderRadius: 1,
80
- bgcolor: 'action.hover',
81
- maxHeight: NOTICE_COLLAPSED_MAX_HEIGHT,
82
- overflow: 'hidden'
83
- }
84
- }, /*#__PURE__*/React.createElement("div", {
85
- ref: contentRef,
86
- dangerouslySetInnerHTML: {
87
- __html: contentHtml
88
- }
89
- })), overflowing && /*#__PURE__*/React.createElement(Button, {
90
- size: "small",
91
- onClick: () => setDialogOpen(true),
92
- sx: {
93
- alignSelf: 'flex-start',
94
- textTransform: 'none',
95
- minWidth: 0,
96
- p: 0
97
- }
98
- }, i18next.t('animal.view_full_field', {
99
- label: field.label
100
- })), /*#__PURE__*/React.createElement(Dialog, {
101
- open: dialogOpen,
102
- onClose: () => setDialogOpen(false),
103
- maxWidth: "sm",
104
- fullWidth: true,
105
- scroll: "paper"
106
- }, /*#__PURE__*/React.createElement(DialogTitle, null, field.label), /*#__PURE__*/React.createElement(DialogContent, {
107
- dividers: true
108
- }, /*#__PURE__*/React.createElement("div", {
109
- dangerouslySetInnerHTML: {
110
- __html: contentHtml
111
- }
112
- })), /*#__PURE__*/React.createElement(DialogActions, null, /*#__PURE__*/React.createElement(Button, {
113
- onClick: () => setDialogOpen(false)
114
- }, i18next.t('common.app.close')))));
115
- };
116
- NoticeFieldBlock.propTypes = {
117
- field: PropTypes.shape({
118
- key: PropTypes.string.isRequired,
119
- label: PropTypes.string.isRequired,
120
- content_html: PropTypes.string,
121
- meta: PropTypes.object
122
- }).isRequired,
123
- i18next: PropTypes.any.isRequired
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'
124
39
  };
125
40
 
126
41
  /**
127
42
  * Flat, non-tabbed block for a lot's "once per reservation" animal fields: no tabs, no per-animal
128
43
  * chrome, no add/remove -- these are single shared values/notices for the whole reservation, not
129
- * one row per animal. Renders two kinds of entries, in whatever order `fields` gives them:
44
+ * one row per animal. Renders any field (any type, including 'notice') whose config opts into
45
+ * `once_per_reservation` -- reuses the exact same `renderFieldTypeControl` that
46
+ * AnimalVehicleDetailsEditor's tab-per-animal grid uses (see fieldTypeControl.jsx, this directory),
47
+ * so a once-per-reservation field looks and behaves identically to its per-animal counterpart, type
48
+ * for type. `once_per_reservation` is independent of `type` -- a 'notice' field can be either
49
+ * once-per-reservation (rendered here) or per-animal (rendered in the tab grid instead), exactly
50
+ * like any other field type.
130
51
  *
131
- * - Any input-type field (text/select/number/textarea/yes_no/date/select_multiple) whose config
132
- * opts into `once_per_reservation` -- reuses the exact same `renderFieldTypeControl` that
133
- * AnimalVehicleDetailsEditor's tab-per-animal grid uses (see fieldTypeControl.jsx, this
134
- * directory), so a once-per-reservation field looks and behaves identically to its per-animal
135
- * counterpart.
136
- * - Any `field_type === 'notice'` field -- a read-only sanitized-HTML display, never an input.
137
- * `once_per_reservation` doesn't apply to notices (they're never per-animal to begin with) and
138
- * is ignored for this type.
139
- *
140
- * `fields` does NOT need to be pre-filtered: this component filters internally (any field that is
141
- * neither a notice nor once_per_reservation is silently skipped), so it's safe to pass the lot's
142
- * whole field list straight through. Passing an already-filtered subset works identically, since
143
- * the filter is idempotent.
52
+ * `fields` does NOT need to be pre-filtered: this component filters internally (any field whose
53
+ * `once_per_reservation` isn't `true` is silently skipped), so it's safe to pass the lot's whole
54
+ * field list straight through. Passing an already-filtered subset works identically, since the
55
+ * filter is idempotent.
144
56
  *
145
57
  * Two usage modes, both through this one component:
146
58
  * - Editable (staff/guest edit flow): pass `onChange`. Inputs render live and call
147
59
  * `onChange(field.key, newValue)`.
148
- * - Read-only (review screen): omit `onChange` entirely. Every input control renders disabled
149
- * (MUI's own disabled/read-only appearance) instead of interactive -- there is no second
150
- * "read-only variant" component to import, this is the same export either way. The notice
151
- * block's "View full ..." link/dialog behaves identically in both modes, since it was never an
152
- * input.
60
+ * - Read-only (review screen): omit `onChange` entirely. Every control renders disabled (MUI's own
61
+ * disabled/read-only appearance) instead of interactive -- there is no second "read-only
62
+ * variant" component to import, this is the same export either way.
153
63
  *
154
64
  * ## Importing the component in your project
155
65
  *
@@ -165,13 +75,15 @@ export const AnimalFieldsOnceBlock = props => {
165
75
  fields,
166
76
  values,
167
77
  onChange,
78
+ isFieldAnswered,
168
79
  className,
169
80
  theme,
170
81
  i18next
171
82
  } = props;
172
83
  const compTheme = theme || useTheme();
173
84
  const defaultTheme = createTheme(compTheme);
174
- const relevantFields = fields.filter(field => field.type === 'notice' || INPUT_FIELD_TYPES.includes(field.type) && readFieldProp(field, 'once_per_reservation') === true);
85
+ const requiredErrorText = fieldType => i18next.t(REQUIRED_ERROR_KEY_BY_TYPE[fieldType] || 'animal.required_error');
86
+ const relevantFields = fields.filter(field => readFieldProp(field, 'once_per_reservation') === true);
175
87
  if (relevantFields.length === 0) return null;
176
88
  return /*#__PURE__*/React.createElement(ThemeProvider, {
177
89
  theme: defaultTheme
@@ -185,16 +97,15 @@ export const AnimalFieldsOnceBlock = props => {
185
97
  gridAutoFlow: 'row dense'
186
98
  }
187
99
  }, relevantFields.map(field => {
188
- if (field.type === 'notice') {
189
- return /*#__PURE__*/React.createElement("div", {
190
- key: field.key,
191
- className: "col-span-full"
192
- }, /*#__PURE__*/React.createElement(NoticeFieldBlock, {
193
- field: field,
194
- i18next: i18next
195
- }));
196
- }
197
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);
198
109
  const labelText = field.required ? `${field.label} *` : field.label;
199
110
  return /*#__PURE__*/React.createElement("div", {
200
111
  key: field.key,
@@ -203,7 +114,8 @@ export const AnimalFieldsOnceBlock = props => {
203
114
  variant: "body2",
204
115
  sx: {
205
116
  fontWeight: 500,
206
- mb: 0.5
117
+ mb: 0.5,
118
+ color: showError ? 'error.main' : 'text.primary'
207
119
  }
208
120
  }, labelText), renderFieldTypeControl({
209
121
  field,
@@ -213,8 +125,8 @@ export const AnimalFieldsOnceBlock = props => {
213
125
  // consumer's own event wiring calls it directly in a test.
214
126
  onChange: onChange ? newValue => onChange(field.key, newValue) : () => {},
215
127
  disabled: !onChange,
216
- error: false,
217
- helperText: '',
128
+ error: showError,
129
+ helperText: showError ? requiredErrorText(field.type) : '',
218
130
  i18next
219
131
  }));
220
132
  }))));
@@ -231,8 +143,9 @@ AnimalFieldsOnceBlock.propTypes = {
231
143
  /** The lot's field configs -- does not need to be pre-filtered, see the component doc above.
232
144
  * Normalized to the same flat shape AnimalVehicleDetailsEditor's `fieldConfig` already uses
233
145
  * (label/options/unit/placeholder resolved by the caller), plus this feature's two additions:
234
- * `once_per_reservation` (input types) and `content_html` (type 'notice'). Both are also read
235
- * from `field.meta.<key>` as a fallback if a caller passes the meta object through unflattened. */
146
+ * `once_per_reservation` (any type, independently admin-configurable) and `content_html` (type
147
+ * 'notice' only, read inside fieldTypeControl.jsx's notice control). Both are also read from
148
+ * `field.meta.<key>` as a fallback if a caller passes the meta object through unflattened. */
236
149
  fields: PropTypes.arrayOf(PropTypes.shape({
237
150
  key: PropTypes.string.isRequired,
238
151
  label: PropTypes.string.isRequired,
@@ -252,6 +165,10 @@ AnimalFieldsOnceBlock.propTypes = {
252
165
  /** (fieldKey, value) => void. Omit entirely to render every input control disabled/read-only
253
166
  * (review-screen usage) -- see the component doc above. */
254
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,
255
172
  /** Classes applied to the component's own wrapper div. */
256
173
  className: PropTypes.string,
257
174
  /** Theme object to use on this component, if none provided then the MUI5 theme provider theme is used. */
@@ -1,6 +1,6 @@
1
1
  function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
2
- import React from 'react';
3
- import { TextField, InputAdornment, MenuItem, ToggleButton, ToggleButtonGroup, Checkbox, ListItemText, Chip } from '@mui/material';
2
+ import React, { useLayoutEffect, useRef, useState } from 'react';
3
+ import { TextField, InputAdornment, MenuItem, ToggleButton, ToggleButtonGroup, Checkbox, ListItemText, Chip, FormControlLabel, FormHelperText, Paper, Button, Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material';
4
4
 
5
5
  // A native <input type="date"> only opens its calendar popup on its own when the browser's tiny
6
6
  // built-in icon is clicked -- clicking anywhere else in the field just places the text caret
@@ -18,10 +18,116 @@ export const openNativeDatePicker = e => {
18
18
  }
19
19
  };
20
20
 
21
+ // Collapsed height for a notice's rich-text content before it needs a "View full ..." link. A
22
+ // handful of lines of admin-entered text/markup fits inside this without ever showing the link at
23
+ // all; anything longer gets clipped here and offered in full through the dialog below.
24
+ const NOTICE_COLLAPSED_MAX_HEIGHT = 160;
25
+
26
+ /**
27
+ * The control for a `notice` field: the admin's sanitized rich text inside a bounded-height
28
+ * container (only overflowing content gets a "View full {label}" link that opens the rest in a
29
+ * Dialog), plus a single "I have read this" checkbox whose checked state IS the field's value --
30
+ * stored the same way a `yes_no` field's boolean value is. Not exported -- only reached through
31
+ * `renderFieldTypeControl`'s `notice` case below, same as every other type-specific control in
32
+ * this switch.
33
+ *
34
+ * The field's own label is deliberately NOT rendered in here: both call sites
35
+ * (AnimalVehicleDetailsEditor's per-item grid and AnimalFieldsOnceBlock's flat layout) already
36
+ * render `field.label` above whatever control this function returns, same as for every other type.
37
+ */
38
+ const NoticeFieldControl = ({
39
+ field,
40
+ value,
41
+ onChange,
42
+ disabled,
43
+ error,
44
+ helperText,
45
+ i18next
46
+ }) => {
47
+ const contentRef = useRef(null);
48
+ const [overflowing, setOverflowing] = useState(false);
49
+ const [dialogOpen, setDialogOpen] = useState(false);
50
+ // Already HTML-sanitized server-side before this ever reaches the client -- safe to render as-is.
51
+ // `field.meta.content_html` fallback covers a caller passing the raw meta object through
52
+ // unflattened, same convention this feature's other `field.<key>` reads already follow.
53
+ const contentHtml = field.content_html !== undefined ? field.content_html : field.meta?.content_html || '';
54
+
55
+ // Measured after mount/paint (useLayoutEffect, not useEffect) so the "View full ..." link never
56
+ // flashes in for a heartbeat on content that turns out to fit. Re-checked on window resize too:
57
+ // the height bound is fixed in CSS, but at a narrower viewport the same HTML can wrap onto more
58
+ // lines and start overflowing where it didn't before.
59
+ useLayoutEffect(() => {
60
+ const checkOverflow = () => {
61
+ const el = contentRef.current;
62
+ if (!el) return;
63
+ setOverflowing(el.scrollHeight > el.clientHeight);
64
+ };
65
+ checkOverflow();
66
+ window.addEventListener('resize', checkOverflow);
67
+ return () => window.removeEventListener('resize', checkOverflow);
68
+ }, [contentHtml]);
69
+ return /*#__PURE__*/React.createElement("div", {
70
+ className: "flex flex-col",
71
+ style: {
72
+ gap: 4
73
+ }
74
+ }, /*#__PURE__*/React.createElement(Paper, {
75
+ variant: "outlined",
76
+ sx: {
77
+ p: 1.5,
78
+ borderRadius: 1,
79
+ bgcolor: 'action.hover',
80
+ maxHeight: NOTICE_COLLAPSED_MAX_HEIGHT,
81
+ overflow: 'hidden'
82
+ }
83
+ }, /*#__PURE__*/React.createElement("div", {
84
+ ref: contentRef,
85
+ dangerouslySetInnerHTML: {
86
+ __html: contentHtml
87
+ }
88
+ })), overflowing && /*#__PURE__*/React.createElement(Button, {
89
+ size: "small",
90
+ disabled: disabled,
91
+ onClick: () => setDialogOpen(true),
92
+ sx: {
93
+ alignSelf: 'flex-start',
94
+ textTransform: 'none',
95
+ minWidth: 0,
96
+ p: 0
97
+ }
98
+ }, i18next.t('animal.view_full_field', {
99
+ label: field.label
100
+ })), /*#__PURE__*/React.createElement(Dialog, {
101
+ open: dialogOpen,
102
+ onClose: () => setDialogOpen(false),
103
+ maxWidth: "sm",
104
+ fullWidth: true,
105
+ scroll: "paper"
106
+ }, /*#__PURE__*/React.createElement(DialogTitle, null, field.label), /*#__PURE__*/React.createElement(DialogContent, {
107
+ dividers: true
108
+ }, /*#__PURE__*/React.createElement("div", {
109
+ dangerouslySetInnerHTML: {
110
+ __html: contentHtml
111
+ }
112
+ })), /*#__PURE__*/React.createElement(DialogActions, null, /*#__PURE__*/React.createElement(Button, {
113
+ onClick: () => setDialogOpen(false)
114
+ }, i18next.t('common.app.close')))), /*#__PURE__*/React.createElement(FormControlLabel, {
115
+ control: /*#__PURE__*/React.createElement(Checkbox, {
116
+ checked: value === true,
117
+ onChange: e => onChange(e.target.checked),
118
+ disabled: disabled,
119
+ color: error ? 'error' : 'primary'
120
+ }),
121
+ label: i18next.t('animal.read_notice_confirmation')
122
+ }), helperText && /*#__PURE__*/React.createElement(FormHelperText, {
123
+ error: error
124
+ }, helperText));
125
+ };
126
+
21
127
  /**
22
128
  * Renders the single form control for one {field, value, onChange} triple across every field type
23
- * this feature knows about: text / number / select / select_multiple / textarea / yes_no / date
24
- * (unknown/legacy types fall back to plain text, same as before). Pulled out of the per-item field
129
+ * this feature knows about: text / number / select / select_multiple / textarea / yes_no / date /
130
+ * notice (unknown/legacy types fall back to plain text, same as before). Pulled out of the per-item field
25
131
  * grid in `index.jsx` (AnimalVehicleDetailsEditor's tab-per-animal editor) so
26
132
  * `AnimalFieldsOnceBlock`'s flat once-per-reservation layout, in the same directory, can render the
27
133
  * exact same controls without a second copy of this switch. Both call sites feed it the same shape
@@ -40,7 +146,8 @@ export const openNativeDatePicker = e => {
40
146
  * Only `key`/`type` and the type-specific extras are read here; `label`/`required` are the
41
147
  * caller's concern (rendered outside this function).
42
148
  * @param {*} params.value - Current value, already shaped for `field.type` (string for
43
- * text/select/number/date, array for select_multiple, boolean|null for yes_no).
149
+ * text/select/number/date, array for select_multiple, boolean|null for yes_no/notice -- for
150
+ * notice this is the "I have read this" checkbox's checked state, not the (read-only) content).
44
151
  * @param {(value: *) => void} params.onChange - Called with the new value in that same shape.
45
152
  * Callers needing an (itemId, key, value) or (key, value) signature wrap it, e.g.
46
153
  * `(v) => onFieldChange(item.id, field.key, v)`.
@@ -158,6 +265,16 @@ export const renderFieldTypeControl = ({
158
265
  value: "no"
159
266
  }, i18next.t('common.app.no')));
160
267
  }
268
+ case 'notice':
269
+ return /*#__PURE__*/React.createElement(NoticeFieldControl, {
270
+ field: field,
271
+ value: value,
272
+ onChange: onChange,
273
+ disabled: disabled,
274
+ error: error,
275
+ helperText: helperText,
276
+ i18next: i18next
277
+ });
161
278
  case 'date':
162
279
  // Native date input, not each host app's own local DateTimeField wrapper -- this component
163
280
  // can't depend on either app's app-local component tree. Value is a plain 'YYYY-MM-DD'
@@ -47,12 +47,17 @@ const KEYS = {
47
47
  // select an option" reads very differently from "please enter a number". Not itemType-specific:
48
48
  // a select field is a select field whether it belongs to an animal or a vehicle. Falls back to
49
49
  // animal.required_error for text/textarea, where the generic "fill this in" copy already fits.
50
+ // 'notice' gets its own message (animal.required_error_notice) rather than the generic one --
51
+ // its required-satisfying condition is stricter than every other type (see isFieldAnswered,
52
+ // injected by the caller): only a CHECKED "I have read this" box counts, so "please fill this in"
53
+ // would read wrong for what's actually a checkbox.
50
54
  const REQUIRED_ERROR_KEY_BY_TYPE = {
51
55
  select: 'animal.required_error_select',
52
56
  select_multiple: 'animal.required_error_select_multiple',
53
57
  number: 'animal.required_error_number',
54
58
  date: 'animal.required_error_date',
55
- yes_no: 'animal.required_error_yes_no'
59
+ yes_no: 'animal.required_error_yes_no',
60
+ notice: 'animal.required_error_notice'
56
61
  };
57
62
 
58
63
  /**
@@ -224,7 +229,7 @@ export const AnimalVehicleDetailsEditor = props => {
224
229
  return nextWrapped;
225
230
  });
226
231
  }, [fieldConfig, wrappedKeys]);
227
- const isFullWidthField = f => ['select', 'textarea', 'select_multiple'].includes(f.type) || wrappedKeys.has(f.key);
232
+ const isFullWidthField = f => ['select', 'textarea', 'select_multiple', 'notice'].includes(f.type) || wrappedKeys.has(f.key);
228
233
  return /*#__PURE__*/React.createElement(ThemeProvider, {
229
234
  theme: defaultTheme
230
235
  }, /*#__PURE__*/React.createElement("div", {
@@ -617,11 +622,14 @@ AnimalVehicleDetailsEditor.propTypes = {
617
622
  key: PropTypes.string.isRequired,
618
623
  label: PropTypes.string.isRequired,
619
624
  required: PropTypes.bool,
620
- type: PropTypes.oneOf(['text', 'number', 'select', 'select_multiple', 'textarea', 'yes_no', 'date']),
625
+ type: PropTypes.oneOf(['text', 'number', 'select', 'select_multiple', 'textarea', 'yes_no', 'date', 'notice']),
621
626
  options: PropTypes.arrayOf(PropTypes.string),
622
627
  unit: PropTypes.string,
623
628
  placeholder: PropTypes.string,
624
- maxLength: PropTypes.number
629
+ maxLength: PropTypes.number,
630
+ /** Type 'notice' only -- the admin's sanitized rich text, read inside fieldTypeControl.jsx's
631
+ * notice control. */
632
+ content_html: PropTypes.string
625
633
  })),
626
634
  /** (type, value) => boolean. Injected rather than owned internally, so each host app's existing
627
635
  * "has this field actually been answered" rule (yes_no's `false` counts, etc.) stays authoritative. */
@@ -39,16 +39,14 @@ export const classifyAnimalFieldValue = (fieldConfig, rawValue) => {
39
39
  options
40
40
  } = fieldConfig;
41
41
 
42
- // notice fields are never per-animal and never have a value to hold in the first place (see the
43
- // shared lot_animal_fields.meta contract) -- always 'unanswered' regardless of any stray content
44
- // that might still be sitting in field_values under this key (e.g. left over from before the
45
- // field was retyped to 'notice'), so a caller filtering on `!== 'unanswered'` never shows a
46
- // spurious 'drifted' warning for a field that was never answerable to begin with. Every consumer
47
- // in the app/ repo already excludes 'notice' from this call entirely (see DetailsSummary.jsx /
48
- // Occupant.js's per-animal vs. once-per-reservation field split), so this is a defensive fallback
49
- // for any other/future caller that passes one through unfiltered.
50
- if (type === 'notice') return 'unanswered';
51
- if (type === 'yes_no') {
42
+ // notice's control is a single "I have read this" checkbox, whose checked state IS the field's
43
+ // value -- stored as a real boolean in field_values, exactly like yes_no. Same classification as
44
+ // yes_no below: a stored `true`/`false` is 'answered' (a stray non-boolean leftover from before
45
+ // the field was retyped to/from 'notice' is 'drifted' rather than silently treated as answered),
46
+ // anything else is 'unanswered'. This is drift-detection shape-matching only -- it does NOT decide
47
+ // whether an unchecked (`false`) box satisfies a *required* notice, which is a stricter rule than
48
+ // yes_no's (see isFieldAnswered, injected by each caller, not this function).
49
+ if (type === 'yes_no' || type === 'notice') {
52
50
  if (typeof rawValue === 'boolean') return 'answered';
53
51
  return hasWrongShapeContent(rawValue) ? 'drifted' : 'unanswered';
54
52
  }
@@ -19,6 +19,8 @@
19
19
  "required_error": "Please fill this in.",
20
20
  "required_for_guests": "Required for guests",
21
21
  "section_intro": "Please tell us a bit about each animal traveling with you.",
22
+ "read_notice_confirmation": "I have read this",
23
+ "required_error_notice": "Please confirm you have read this.",
22
24
  "required_error_select": "Please select an option.",
23
25
  "required_error_select_multiple": "Please select at least one option.",
24
26
  "required_error_number": "Please enter a number.",
@@ -2469,7 +2471,7 @@
2469
2471
  "ask_once_per_reservation": "Ask once per reservation",
2470
2472
  "once_per_reservation_helper": "Shown once for the whole reservation instead of once per animal.",
2471
2473
  "notice_content_label": "Notice content",
2472
- "notice_preview_label": "Preview",
2474
+ "notice_empty_hint": "Add your notice text above to see it here.",
2473
2475
  "options_label": "Options",
2474
2476
  "add_option": "Add Option",
2475
2477
  "unit_label": "Unit",
@@ -19,6 +19,8 @@
19
19
  "required_error": "Por favor, complete este campo.",
20
20
  "required_for_guests": "Requerido para huéspedes",
21
21
  "section_intro": "Por favor, cuéntenos un poco sobre cada animal que viaja con usted.",
22
+ "read_notice_confirmation": "He leído esto",
23
+ "required_error_notice": "Por favor, confirme que ha leído esto.",
22
24
  "required_error_select": "Por favor, seleccione una opción.",
23
25
  "required_error_select_multiple": "Por favor, seleccione al menos una opción.",
24
26
  "required_error_number": "Por favor, ingrese un número.",
@@ -2469,7 +2471,7 @@
2469
2471
  "ask_once_per_reservation": "Preguntar una sola vez por reserva",
2470
2472
  "once_per_reservation_helper": "Se muestra una sola vez para toda la reserva, en lugar de una vez por animal.",
2471
2473
  "notice_content_label": "Contenido del aviso",
2472
- "notice_preview_label": "Vista previa",
2474
+ "notice_empty_hint": "Agregue el texto de su aviso arriba para verlo aquí.",
2473
2475
  "options_label": "Opciones",
2474
2476
  "add_option": "Agregar opción",
2475
2477
  "unit_label": "Unidad",
@@ -19,6 +19,8 @@
19
19
  "required_error": "Veuillez remplir ce champ.",
20
20
  "required_for_guests": "Requis pour les clients",
21
21
  "section_intro": "Merci de nous donner quelques informations sur chaque animal qui vous accompagne.",
22
+ "read_notice_confirmation": "J'ai lu ceci",
23
+ "required_error_notice": "Veuillez confirmer que vous avez lu ceci.",
22
24
  "required_error_select": "Veuillez sélectionner une option.",
23
25
  "required_error_select_multiple": "Veuillez sélectionner au moins une option.",
24
26
  "required_error_number": "Veuillez entrer un nombre.",
@@ -2469,7 +2471,7 @@
2469
2471
  "ask_once_per_reservation": "Demander une seule fois par réservation",
2470
2472
  "once_per_reservation_helper": "Affiché une seule fois pour toute la réservation, plutôt que pour chaque animal.",
2471
2473
  "notice_content_label": "Contenu de l'avis",
2472
- "notice_preview_label": "Aperçu",
2474
+ "notice_empty_hint": "Ajoutez le texte de votre avis ci-dessus pour le voir ici.",
2473
2475
  "options_label": "Options",
2474
2476
  "add_option": "Ajouter une option",
2475
2477
  "unit_label": "Unité",
@@ -1 +1 @@
1
- *,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#2196f380;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.visible{visibility:visible}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.-top-2{top:-.2rem}.-top-6{top:-.6rem}.bottom-0{bottom:0}.left-0{left:0}.right-0{right:0}.top-0{top:0}.top-20{top:2rem}.top-64{top:6.4rem}.z-10{z-index:10}.z-99{z-index:99}.col-span-full{grid-column:1/-1}.m-10{margin:1rem}.m-12{margin:1.2rem}.m-24{margin:2.4rem}.m-4{margin:.4rem}.m-6{margin:.6rem}.m-auto{margin:auto}.mx-0{margin-left:0;margin-right:0}.mx-12{margin-left:1.2rem;margin-right:1.2rem}.mx-2{margin-left:.2rem;margin-right:.2rem}.mx-4{margin-left:.4rem;margin-right:.4rem}.mx-6{margin-left:.6rem;margin-right:.6rem}.mx-8{margin-left:.8rem;margin-right:.8rem}.mx-auto{margin-left:auto;margin-right:auto}.my-12{margin-top:1.2rem;margin-bottom:1.2rem}.my-24{margin-top:2.4rem;margin-bottom:2.4rem}.my-4{margin-top:.4rem;margin-bottom:.4rem}.my-6{margin-top:.6rem;margin-bottom:.6rem}.my-auto{margin-top:auto;margin-bottom:auto}.-ml-10{margin-left:-1rem}.mb-0{margin-bottom:0}.mb-12{margin-bottom:1.2rem}.mb-2{margin-bottom:.2rem}.mb-24{margin-bottom:2.4rem}.mb-6{margin-bottom:.6rem}.mb-8{margin-bottom:.8rem}.ml-0{margin-left:0}.ml-1{margin-left:.1rem}.ml-12{margin-left:1.2rem}.ml-24{margin-left:2.4rem}.ml-6{margin-left:.6rem}.ml-8{margin-left:.8rem}.mr-0{margin-right:0}.mr-12{margin-right:1.2rem}.mr-24{margin-right:2.4rem}.mr-6{margin-right:.6rem}.mt-0{margin-top:0}.mt-12{margin-top:1.2rem}.mt-24{margin-top:2.4rem}.mt-4{margin-top:.4rem}.mt-6{margin-top:.6rem}.mt-auto{margin-top:auto}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-136{height:13.6rem}.h-200{height:20rem}.h-24{height:2.4rem}.h-400{height:40rem}.h-auto{height:auto}.h-full{height:100%}.h-screen{height:100vh}.max-h-32{max-height:3.2rem}.max-h-512{max-height:51.2rem}.min-h-160{min-height:16rem}.min-h-224{min-height:22.4rem}.min-h-256{min-height:25.6rem}.w-1\/2{width:50%}.w-2\/5{width:40%}.w-24{width:2.4rem}.w-3\/4{width:75%}.w-auto{width:auto}.w-full{width:100%}.max-w-full{max-width:100%}.max-w-min{max-width:-moz-min-content;max-width:min-content}.flex-shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.grow{flex-grow:1}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.resize{resize:both}.list-disc{list-style-type:disc}.list-none{list-style-type:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-rows-2{grid-template-rows:repeat(2,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-around{justify-content:space-around}.gap-4{gap:.4rem}.gap-6{gap:.6rem}.space-x-10>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-y-10>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-scroll{overflow:scroll}.overscroll-none{overscroll-behavior:none}.truncate{overflow:hidden;white-space:nowrap}.overflow-ellipsis,.truncate{text-overflow:ellipsis}.whitespace-normal{white-space:normal}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-line{white-space:pre-line}.rounded{border-radius:.4rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.8rem}.rounded-md{border-radius:.6rem}.rounded-t-md{border-top-left-radius:.6rem;border-top-right-radius:.6rem}.rounded-bl-lg{border-bottom-left-radius:.8rem}.rounded-br-lg{border-bottom-right-radius:.8rem}.rounded-tl-lg{border-top-left-radius:.8rem}.rounded-tr-lg{border-top-right-radius:.8rem}.border{border-width:1px}.border-2{border-width:2px}.border-t-8{border-top-width:8px}.border-transparent{border-color:#0000}.bg-black{--tw-bg-opacity:1;background-color:rgb(34 41 47/var(--tw-bg-opacity,1))}.bg-blue{--tw-bg-opacity:1;background-color:rgb(33 150 243/var(--tw-bg-opacity,1))}.bg-gray-800{--tw-bg-opacity:1;background-color:rgb(66 66 66/var(--tw-bg-opacity,1))}.bg-green-600{--tw-bg-opacity:1;background-color:rgb(67 160 71/var(--tw-bg-opacity,1))}.bg-grey-100{--tw-bg-opacity:1;background-color:rgb(245 245 245/var(--tw-bg-opacity,1))}.bg-grey-200{--tw-bg-opacity:1;background-color:rgb(238 238 238/var(--tw-bg-opacity,1))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-yellow-100\/50{background-color:#fff9c480}.bg-cover{background-size:cover}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0{padding:0}.p-10{padding:1rem}.p-12{padding:1.2rem}.p-24{padding:2.4rem}.p-6{padding:.6rem}.px-2{padding-left:.2rem;padding-right:.2rem}.px-4{padding-left:.4rem;padding-right:.4rem}.px-6{padding-left:.6rem;padding-right:.6rem}.px-8{padding-left:.8rem;padding-right:.8rem}.py-2{padding-top:.2rem;padding-bottom:.2rem}.py-4{padding-top:.4rem;padding-bottom:.4rem}.py-6{padding-top:.6rem;padding-bottom:.6rem}.pb-4{padding-bottom:.4rem}.pl-0{padding-left:0}.pl-4{padding-left:.4rem}.pl-8{padding-left:.8rem}.pr-0{padding-right:0}.pr-6{padding-right:.6rem}.pt-0{padding-top:0}.pt-12{padding-top:1.2rem}.pt-4{padding-top:.4rem}.text-left{text-align:left}.text-center{text-align:center}.font-sans{font-family:Muli,Roboto,-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.font-serif{font-family:Georgia,Cambria,Times New Roman,Times,serif}.text-14{font-size:1.4rem}.text-16{font-size:1.6rem}.text-17{font-size:1.7rem}.text-24,.text-2xl{font-size:2.4rem}.text-2xl{line-height:3.2rem}.text-32{font-size:3.2rem}.text-4xl{font-size:3.6rem;line-height:4rem}.text-5xl{font-size:4.8rem;line-height:1}.text-6xl{font-size:6rem;line-height:1}.text-base{font-size:1.6rem;line-height:2.4rem}.text-sm{font-size:1.4rem;line-height:2rem}.text-xl{font-size:2rem;line-height:2.8rem}.font-500{font-weight:500}.font-600{font-weight:600}.font-800{font-weight:800}.font-900{font-weight:900}.font-bold{font-weight:700}.font-extrabold{font-weight:800}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.capitalize{text-transform:capitalize}.normal-case{text-transform:none}.leading-none{line-height:1}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.text-gray-300{--tw-text-opacity:1;color:rgb(224 224 224/var(--tw-text-opacity,1))}.text-gray-500{--tw-text-opacity:1;color:rgb(158 158 158/var(--tw-text-opacity,1))}.text-gray-600{--tw-text-opacity:1;color:rgb(117 117 117/var(--tw-text-opacity,1))}.text-gray-700{--tw-text-opacity:1;color:rgb(97 97 97/var(--tw-text-opacity,1))}.text-green-600{--tw-text-opacity:1;color:rgb(67 160 71/var(--tw-text-opacity,1))}.text-green-700{--tw-text-opacity:1;color:rgb(56 142 60/var(--tw-text-opacity,1))}.text-grey-800{--tw-text-opacity:1;color:rgb(66 66 66/var(--tw-text-opacity,1))}.text-orange-600{--tw-text-opacity:1;color:rgb(251 140 0/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.underline{text-decoration-line:underline}.line-through{text-decoration-line:line-through}.no-underline{text-decoration-line:none}.decoration-gray-500{text-decoration-color:#9e9e9e}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.shadow{--tw-shadow:0 1px 3px 0 #0000001a,0 1px 2px 0 #0000000f;--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px 0 var(--tw-shadow-color)}.shadow,.shadow-1{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-1{--tw-shadow:0px 2px 1px -1px #0003,0px 1px 1px 0px #00000024,0px 1px 3px 0px #0000001f;--tw-shadow-colored:0px 2px 1px -1px var(--tw-shadow-color),0px 1px 1px 0px var(--tw-shadow-color),0px 1px 3px 0px var(--tw-shadow-color)}.shadow-2{--tw-shadow:0px 3px 1px -2px #0003,0px 2px 2px 0px #00000024,0px 1px 5px 0px #0000001f;--tw-shadow-colored:0px 3px 1px -2px var(--tw-shadow-color),0px 2px 2px 0px var(--tw-shadow-color),0px 1px 5px 0px var(--tw-shadow-color)}.shadow-2,.shadow-md{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -1px var(--tw-shadow-color)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.blur{--tw-blur:blur(8px)}.blur,.contrast-0{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.contrast-0{--tw-contrast:contrast(0)}.drop-shadow{--tw-drop-shadow:drop-shadow(0 1px 2px #0000001a) drop-shadow(0 1px 1px #0000000f)}.drop-shadow,.drop-shadow-none{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.drop-shadow-none{--tw-drop-shadow:drop-shadow(0 0 #0000)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-300{transition-duration:.3s}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.hover\:translate-y-1:hover{--tw-translate-y:0.1rem}.hover\:rotate-3:hover,.hover\:translate-y-1:hover{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:rotate-3:hover{--tw-rotate:3deg}.hover\:skew-y-3:hover{--tw-skew-y:3deg}.hover\:scale-105:hover,.hover\:skew-y-3:hover{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:scale-105:hover{--tw-scale-x:1.05;--tw-scale-y:1.05}.hover\:list-disc:hover{list-style-type:disc}.hover\:bg-gray-100\/75:hover{background-color:#f5f5f5bf}.hover\:bg-green-700:hover{--tw-bg-opacity:1;background-color:rgb(56 142 60/var(--tw-bg-opacity,1))}.hover\:bg-grey-300:hover{--tw-bg-opacity:1;background-color:rgb(224 224 224/var(--tw-bg-opacity,1))}.hover\:bg-indigo-100\/25:hover{background-color:#c5cae940}.hover\:text-gray-700:hover{--tw-text-opacity:1;color:rgb(97 97 97/var(--tw-text-opacity,1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:brightness-75:hover{--tw-brightness:brightness(.75)}.hover\:brightness-75:hover,.hover\:drop-shadow-2xl:hover{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.hover\:drop-shadow-2xl:hover{--tw-drop-shadow:drop-shadow(0 25px 25px #00000026)}.focus\:outline-none:focus{outline:2px solid #0000;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-green-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(76 175 80/var(--tw-ring-opacity,1))}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px}@media (min-width:600px){.sm\:p-8{padding:.8rem}}@media (min-width:960px){.md\:mx-auto{margin-left:auto;margin-right:auto}.md\:ml-12{margin-left:1.2rem}.md\:flex{display:flex}.md\:hidden{display:none}.md\:max-w-288{max-width:28.8rem}.md\:flex-row{flex-direction:row}.md\:flex-col{flex-direction:column}.md\:pt-0{padding-top:0}}@media (prefers-color-scheme:dark){.dark\:text-gray-500{--tw-text-opacity:1;color:rgb(158 158 158/var(--tw-text-opacity,1))}.dark\:text-green-500{--tw-text-opacity:1;color:rgb(76 175 80/var(--tw-text-opacity,1))}.dark\:text-orange-500{--tw-text-opacity:1;color:rgb(255 152 0/var(--tw-text-opacity,1))}}
1
+ *,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#2196f380;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.visible{visibility:visible}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.-top-2{top:-.2rem}.-top-6{top:-.6rem}.bottom-0{bottom:0}.left-0{left:0}.right-0{right:0}.top-0{top:0}.top-20{top:2rem}.top-64{top:6.4rem}.z-10{z-index:10}.z-99{z-index:99}.col-span-full{grid-column:1/-1}.m-10{margin:1rem}.m-12{margin:1.2rem}.m-24{margin:2.4rem}.m-4{margin:.4rem}.m-6{margin:.6rem}.m-auto{margin:auto}.mx-0{margin-left:0;margin-right:0}.mx-12{margin-left:1.2rem;margin-right:1.2rem}.mx-2{margin-left:.2rem;margin-right:.2rem}.mx-4{margin-left:.4rem;margin-right:.4rem}.mx-6{margin-left:.6rem;margin-right:.6rem}.mx-8{margin-left:.8rem;margin-right:.8rem}.mx-auto{margin-left:auto;margin-right:auto}.my-12{margin-top:1.2rem;margin-bottom:1.2rem}.my-24{margin-top:2.4rem;margin-bottom:2.4rem}.my-4{margin-top:.4rem;margin-bottom:.4rem}.my-6{margin-top:.6rem;margin-bottom:.6rem}.my-auto{margin-top:auto;margin-bottom:auto}.-ml-10{margin-left:-1rem}.mb-0{margin-bottom:0}.mb-12{margin-bottom:1.2rem}.mb-2{margin-bottom:.2rem}.mb-24{margin-bottom:2.4rem}.mb-6{margin-bottom:.6rem}.mb-8{margin-bottom:.8rem}.ml-0{margin-left:0}.ml-1{margin-left:.1rem}.ml-12{margin-left:1.2rem}.ml-24{margin-left:2.4rem}.ml-6{margin-left:.6rem}.ml-8{margin-left:.8rem}.mr-0{margin-right:0}.mr-12{margin-right:1.2rem}.mr-24{margin-right:2.4rem}.mr-6{margin-right:.6rem}.mt-0{margin-top:0}.mt-12{margin-top:1.2rem}.mt-24{margin-top:2.4rem}.mt-4{margin-top:.4rem}.mt-6{margin-top:.6rem}.mt-auto{margin-top:auto}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-136{height:13.6rem}.h-200{height:20rem}.h-24{height:2.4rem}.h-400{height:40rem}.h-auto{height:auto}.h-full{height:100%}.h-screen{height:100vh}.max-h-32{max-height:3.2rem}.max-h-512{max-height:51.2rem}.min-h-160{min-height:16rem}.min-h-224{min-height:22.4rem}.min-h-256{min-height:25.6rem}.w-1\/2{width:50%}.w-2\/5{width:40%}.w-24{width:2.4rem}.w-3\/4{width:75%}.w-auto{width:auto}.w-full{width:100%}.max-w-full{max-width:100%}.max-w-min{max-width:-moz-min-content;max-width:min-content}.flex-shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.grow{flex-grow:1}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.resize{resize:both}.list-disc{list-style-type:disc}.list-none{list-style-type:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-rows-2{grid-template-rows:repeat(2,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-around{justify-content:space-around}.gap-4{gap:.4rem}.gap-6{gap:.6rem}.space-x-10>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-y-10>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-scroll{overflow:scroll}.overscroll-none{overscroll-behavior:none}.truncate{overflow:hidden;white-space:nowrap}.overflow-ellipsis,.truncate{text-overflow:ellipsis}.whitespace-normal{white-space:normal}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-line{white-space:pre-line}.rounded{border-radius:.4rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.8rem}.rounded-md{border-radius:.6rem}.rounded-t-md{border-top-left-radius:.6rem;border-top-right-radius:.6rem}.rounded-bl-lg{border-bottom-left-radius:.8rem}.rounded-br-lg{border-bottom-right-radius:.8rem}.rounded-tl-lg{border-top-left-radius:.8rem}.rounded-tr-lg{border-top-right-radius:.8rem}.border{border-width:1px}.border-2{border-width:2px}.border-t-8{border-top-width:8px}.border-transparent{border-color:#0000}.bg-black{--tw-bg-opacity:1;background-color:rgb(34 41 47/var(--tw-bg-opacity,1))}.bg-blue{--tw-bg-opacity:1;background-color:rgb(33 150 243/var(--tw-bg-opacity,1))}.bg-gray-800{--tw-bg-opacity:1;background-color:rgb(66 66 66/var(--tw-bg-opacity,1))}.bg-green-600{--tw-bg-opacity:1;background-color:rgb(67 160 71/var(--tw-bg-opacity,1))}.bg-grey-100{--tw-bg-opacity:1;background-color:rgb(245 245 245/var(--tw-bg-opacity,1))}.bg-grey-200{--tw-bg-opacity:1;background-color:rgb(238 238 238/var(--tw-bg-opacity,1))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-yellow-100\/50{background-color:#fff9c480}.bg-cover{background-size:cover}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0{padding:0}.p-10{padding:1rem}.p-12{padding:1.2rem}.p-24{padding:2.4rem}.p-6{padding:.6rem}.px-2{padding-left:.2rem;padding-right:.2rem}.px-4{padding-left:.4rem;padding-right:.4rem}.px-6{padding-left:.6rem;padding-right:.6rem}.px-8{padding-left:.8rem;padding-right:.8rem}.py-2{padding-top:.2rem;padding-bottom:.2rem}.py-4{padding-top:.4rem;padding-bottom:.4rem}.py-6{padding-top:.6rem;padding-bottom:.6rem}.pb-4{padding-bottom:.4rem}.pl-0{padding-left:0}.pl-4{padding-left:.4rem}.pl-8{padding-left:.8rem}.pr-0{padding-right:0}.pr-6{padding-right:.6rem}.pt-0{padding-top:0}.pt-12{padding-top:1.2rem}.pt-4{padding-top:.4rem}.text-left{text-align:left}.text-center{text-align:center}.font-sans{font-family:Muli,Roboto,-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.font-serif{font-family:Georgia,Cambria,Times New Roman,Times,serif}.text-14{font-size:1.4rem}.text-16{font-size:1.6rem}.text-17{font-size:1.7rem}.text-24,.text-2xl{font-size:2.4rem}.text-2xl{line-height:3.2rem}.text-32{font-size:3.2rem}.text-4xl{font-size:3.6rem;line-height:4rem}.text-5xl{font-size:4.8rem;line-height:1}.text-6xl{font-size:6rem;line-height:1}.text-base{font-size:1.6rem;line-height:2.4rem}.text-sm{font-size:1.4rem;line-height:2rem}.text-xl{font-size:2rem;line-height:2.8rem}.font-500{font-weight:500}.font-600{font-weight:600}.font-800{font-weight:800}.font-900{font-weight:900}.font-bold{font-weight:700}.font-extrabold{font-weight:800}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.capitalize{text-transform:capitalize}.normal-case{text-transform:none}.leading-none{line-height:1}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.text-gray-300{--tw-text-opacity:1;color:rgb(224 224 224/var(--tw-text-opacity,1))}.text-gray-500{--tw-text-opacity:1;color:rgb(158 158 158/var(--tw-text-opacity,1))}.text-gray-600{--tw-text-opacity:1;color:rgb(117 117 117/var(--tw-text-opacity,1))}.text-gray-700{--tw-text-opacity:1;color:rgb(97 97 97/var(--tw-text-opacity,1))}.text-green-600{--tw-text-opacity:1;color:rgb(67 160 71/var(--tw-text-opacity,1))}.text-green-700{--tw-text-opacity:1;color:rgb(56 142 60/var(--tw-text-opacity,1))}.text-grey-800{--tw-text-opacity:1;color:rgb(66 66 66/var(--tw-text-opacity,1))}.text-orange-600{--tw-text-opacity:1;color:rgb(251 140 0/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.underline{text-decoration-line:underline}.line-through{text-decoration-line:line-through}.no-underline{text-decoration-line:none}.decoration-gray-500{text-decoration-color:#9e9e9e}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.shadow{--tw-shadow:0 1px 3px 0 #0000001a,0 1px 2px 0 #0000000f;--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px 0 var(--tw-shadow-color)}.shadow,.shadow-1{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-1{--tw-shadow:0px 2px 1px -1px #0003,0px 1px 1px 0px #00000024,0px 1px 3px 0px #0000001f;--tw-shadow-colored:0px 2px 1px -1px var(--tw-shadow-color),0px 1px 1px 0px var(--tw-shadow-color),0px 1px 3px 0px var(--tw-shadow-color)}.shadow-2{--tw-shadow:0px 3px 1px -2px #0003,0px 2px 2px 0px #00000024,0px 1px 5px 0px #0000001f;--tw-shadow-colored:0px 3px 1px -2px var(--tw-shadow-color),0px 2px 2px 0px var(--tw-shadow-color),0px 1px 5px 0px var(--tw-shadow-color)}.shadow-2,.shadow-md{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -1px var(--tw-shadow-color)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.blur{--tw-blur:blur(8px)}.blur,.contrast-0{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.contrast-0{--tw-contrast:contrast(0)}.drop-shadow{--tw-drop-shadow:drop-shadow(0 1px 2px #0000001a) drop-shadow(0 1px 1px #0000000f)}.drop-shadow,.drop-shadow-none{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.drop-shadow-none{--tw-drop-shadow:drop-shadow(0 0 #0000)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-300{transition-duration:.3s}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.hover\:translate-y-1:hover{--tw-translate-y:0.1rem}.hover\:rotate-3:hover,.hover\:translate-y-1:hover{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:rotate-3:hover{--tw-rotate:3deg}.hover\:skew-y-3:hover{--tw-skew-y:3deg}.hover\:scale-105:hover,.hover\:skew-y-3:hover{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:scale-105:hover{--tw-scale-x:1.05;--tw-scale-y:1.05}.hover\:list-disc:hover{list-style-type:disc}.hover\:bg-gray-100\/75:hover{background-color:#f5f5f5bf}.hover\:bg-green-700:hover{--tw-bg-opacity:1;background-color:rgb(56 142 60/var(--tw-bg-opacity,1))}.hover\:bg-grey-300:hover{--tw-bg-opacity:1;background-color:rgb(224 224 224/var(--tw-bg-opacity,1))}.hover\:bg-indigo-100\/25:hover{background-color:#c5cae940}.hover\:text-gray-700:hover{--tw-text-opacity:1;color:rgb(97 97 97/var(--tw-text-opacity,1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:brightness-75:hover{--tw-brightness:brightness(.75)}.hover\:brightness-75:hover,.hover\:drop-shadow-2xl:hover{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.hover\:drop-shadow-2xl:hover{--tw-drop-shadow:drop-shadow(0 25px 25px #00000026)}.focus\:outline-none:focus{outline:2px solid #0000;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-green-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(76 175 80/var(--tw-ring-opacity,1))}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px}@media (min-width:600px){.sm\:p-8{padding:.8rem}}@media (min-width:960px){.md\:mx-auto{margin-left:auto;margin-right:auto}.md\:ml-12{margin-left:1.2rem}.md\:flex{display:flex}.md\:hidden{display:none}.md\:max-w-288{max-width:28.8rem}.md\:flex-row{flex-direction:row}.md\:flex-col{flex-direction:column}.md\:pt-0{padding-top:0}}@media (prefers-color-scheme:dark){.dark\:text-gray-500{--tw-text-opacity:1;color:rgb(158 158 158/var(--tw-text-opacity,1))}.dark\:text-green-500{--tw-text-opacity:1;color:rgb(76 175 80/var(--tw-text-opacity,1))}.dark\:text-orange-500{--tw-text-opacity:1;color:rgb(255 152 0/var(--tw-text-opacity,1))}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pcm-shared-components",
3
- "version": "2.1.405",
3
+ "version": "2.1.407",
4
4
  "private": false,
5
5
  "main": "dist/index.js",
6
6
  "babel": {