pcm-shared-components 2.1.404 → 2.1.406
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/AnimalFieldsOnceBlock.js +32 -146
- package/dist/components/Reservation/AnimalVehicleDetailsEditor/fieldTypeControl.js +122 -5
- package/dist/components/Reservation/AnimalVehicleDetailsEditor/index.js +12 -4
- package/dist/components/Reservation/animalFieldDrift.js +8 -10
- package/dist/languages/en/translations.json +3 -1
- package/dist/languages/es/translations.json +3 -1
- package/dist/languages/fr/translations.json +3 -1
- package/dist/styles/tailwind.css +1 -1
- package/package.json +1 -1
|
@@ -1,155 +1,49 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import { Typography
|
|
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
|
|
21
|
-
// index.jsx) -- once-per-reservation
|
|
22
|
-
// measurement machinery needed to keep a large
|
|
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
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
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
|
|
124
|
-
};
|
|
125
|
-
|
|
126
25
|
/**
|
|
127
26
|
* Flat, non-tabbed block for a lot's "once per reservation" animal fields: no tabs, no per-animal
|
|
128
27
|
* chrome, no add/remove -- these are single shared values/notices for the whole reservation, not
|
|
129
|
-
* one row per animal. Renders
|
|
28
|
+
* one row per animal. Renders any field (any type, including 'notice') whose config opts into
|
|
29
|
+
* `once_per_reservation` -- reuses the exact same `renderFieldTypeControl` that
|
|
30
|
+
* AnimalVehicleDetailsEditor's tab-per-animal grid uses (see fieldTypeControl.jsx, this directory),
|
|
31
|
+
* so a once-per-reservation field looks and behaves identically to its per-animal counterpart, type
|
|
32
|
+
* for type. `once_per_reservation` is independent of `type` -- a 'notice' field can be either
|
|
33
|
+
* once-per-reservation (rendered here) or per-animal (rendered in the tab grid instead), exactly
|
|
34
|
+
* like any other field type.
|
|
130
35
|
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
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.
|
|
36
|
+
* `fields` does NOT need to be pre-filtered: this component filters internally (any field whose
|
|
37
|
+
* `once_per_reservation` isn't `true` is silently skipped), so it's safe to pass the lot's whole
|
|
38
|
+
* field list straight through. Passing an already-filtered subset works identically, since the
|
|
39
|
+
* filter is idempotent.
|
|
144
40
|
*
|
|
145
41
|
* Two usage modes, both through this one component:
|
|
146
42
|
* - Editable (staff/guest edit flow): pass `onChange`. Inputs render live and call
|
|
147
43
|
* `onChange(field.key, newValue)`.
|
|
148
|
-
* - Read-only (review screen): omit `onChange` entirely. Every
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
* block's "View full ..." link/dialog behaves identically in both modes, since it was never an
|
|
152
|
-
* input.
|
|
44
|
+
* - Read-only (review screen): omit `onChange` entirely. Every control renders disabled (MUI's own
|
|
45
|
+
* disabled/read-only appearance) instead of interactive -- there is no second "read-only
|
|
46
|
+
* variant" component to import, this is the same export either way.
|
|
153
47
|
*
|
|
154
48
|
* ## Importing the component in your project
|
|
155
49
|
*
|
|
@@ -171,7 +65,7 @@ export const AnimalFieldsOnceBlock = props => {
|
|
|
171
65
|
} = props;
|
|
172
66
|
const compTheme = theme || useTheme();
|
|
173
67
|
const defaultTheme = createTheme(compTheme);
|
|
174
|
-
const relevantFields = fields.filter(field =>
|
|
68
|
+
const relevantFields = fields.filter(field => readFieldProp(field, 'once_per_reservation') === true);
|
|
175
69
|
if (relevantFields.length === 0) return null;
|
|
176
70
|
return /*#__PURE__*/React.createElement(ThemeProvider, {
|
|
177
71
|
theme: defaultTheme
|
|
@@ -185,15 +79,6 @@ export const AnimalFieldsOnceBlock = props => {
|
|
|
185
79
|
gridAutoFlow: 'row dense'
|
|
186
80
|
}
|
|
187
81
|
}, 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
82
|
const rawValue = values?.[field.key] ?? (field.type === 'select_multiple' ? [] : '');
|
|
198
83
|
const labelText = field.required ? `${field.label} *` : field.label;
|
|
199
84
|
return /*#__PURE__*/React.createElement("div", {
|
|
@@ -231,8 +116,9 @@ AnimalFieldsOnceBlock.propTypes = {
|
|
|
231
116
|
/** The lot's field configs -- does not need to be pre-filtered, see the component doc above.
|
|
232
117
|
* Normalized to the same flat shape AnimalVehicleDetailsEditor's `fieldConfig` already uses
|
|
233
118
|
* (label/options/unit/placeholder resolved by the caller), plus this feature's two additions:
|
|
234
|
-
* `once_per_reservation` (
|
|
235
|
-
*
|
|
119
|
+
* `once_per_reservation` (any type, independently admin-configurable) and `content_html` (type
|
|
120
|
+
* 'notice' only, read inside fieldTypeControl.jsx's notice control). Both are also read from
|
|
121
|
+
* `field.meta.<key>` as a fallback if a caller passes the meta object through unflattened. */
|
|
236
122
|
fields: PropTypes.arrayOf(PropTypes.shape({
|
|
237
123
|
key: PropTypes.string.isRequired,
|
|
238
124
|
label: PropTypes.string.isRequired,
|
|
@@ -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
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
// field was retyped to 'notice'
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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é",
|
package/dist/styles/tailwind.css
CHANGED
|
@@ -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))}}
|