@bsol-oss/react-datatable5 12.0.0-beta.8 → 12.0.0-beta.80
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/README.md +224 -5
- package/dist/index.d.ts +606 -96
- package/dist/index.js +2679 -610
- package/dist/index.mjs +2683 -619
- package/dist/types/components/DataTable/DataTable.d.ts +12 -7
- package/dist/types/components/DataTable/DataTableServer.d.ts +6 -4
- package/dist/types/components/DataTable/DefaultTable.d.ts +12 -14
- package/dist/types/components/DataTable/DefaultTableServer.d.ts +23 -0
- package/dist/types/components/DataTable/context/DataTableContext.d.ts +21 -3
- package/dist/types/components/DataTable/context/useDataTableContext.d.ts +2 -2
- package/dist/types/components/DataTable/controls/ReloadButton.d.ts +1 -2
- package/dist/types/components/DataTable/controls/ResetFilteringButton.d.ts +1 -4
- package/dist/types/components/DataTable/controls/ResetSelectionButton.d.ts +1 -4
- package/dist/types/components/DataTable/controls/ResetSortingButton.d.ts +1 -4
- package/dist/types/components/DataTable/controls/TableControls.d.ts +10 -2
- package/dist/types/components/DataTable/display/Table.d.ts +3 -3
- package/dist/types/components/DataTable/display/TableBody.d.ts +1 -2
- package/dist/types/components/DataTable/display/TableBodySkeleton.d.ts +5 -0
- package/dist/types/components/DataTable/display/TableCardContainer.d.ts +6 -3
- package/dist/types/components/DataTable/display/TableDataDisplay.d.ts +6 -1
- package/dist/types/components/DataTable/display/TableFooter.d.ts +1 -5
- package/dist/types/components/DataTable/display/TableHeader.d.ts +46 -8
- package/dist/types/components/DataTable/useDataTableServer.d.ts +55 -3
- package/dist/types/components/DatePicker/DatePicker.d.ts +23 -0
- package/dist/types/components/DatePicker/DateTimePicker.d.ts +11 -0
- package/dist/types/components/DatePicker/DurationPicker.d.ts +12 -0
- package/dist/types/components/DatePicker/IsoTimePicker.d.ts +16 -0
- package/dist/types/components/DatePicker/PickerDemo.d.ts +1 -0
- package/dist/types/components/DatePicker/UniversalPicker.d.ts +9 -0
- package/dist/types/components/DatePicker/index.d.ts +7 -0
- package/dist/types/components/Filter/TagFilter.d.ts +5 -1
- package/dist/types/components/Form/SchemaFormContext.d.ts +22 -6
- package/dist/types/components/Form/components/FileDropzone.d.ts +2 -2
- package/dist/types/components/Form/components/core/DefaultForm.d.ts +1 -0
- package/dist/types/components/Form/components/core/FormBody.d.ts +2 -1
- package/dist/types/components/Form/components/core/FormRoot.d.ts +21 -8
- package/dist/types/components/Form/components/fields/BooleanPicker.d.ts +1 -1
- package/dist/types/components/Form/components/fields/ColumnRenderer.d.ts +3 -2
- package/dist/types/components/Form/components/fields/CustomInput.d.ts +8 -0
- package/dist/types/components/Form/components/fields/DatePicker.d.ts +2 -7
- package/dist/types/components/Form/components/fields/DateRangePicker.d.ts +2 -0
- package/dist/types/components/Form/components/fields/DateTimePicker.d.ts +2 -0
- package/dist/types/components/Form/components/fields/EnumPicker.d.ts +3 -2
- package/dist/types/components/Form/components/fields/FilePicker.d.ts +2 -5
- package/dist/types/components/Form/components/fields/IdPicker.d.ts +1 -1
- package/dist/types/components/Form/components/fields/NumberInputField.d.ts +1 -1
- package/dist/types/components/Form/components/fields/ObjectInput.d.ts +1 -1
- package/dist/types/components/Form/components/fields/RecordInput.d.ts +1 -1
- package/dist/types/components/Form/components/fields/SchemaRenderer.d.ts +1 -1
- package/dist/types/components/Form/components/fields/StringInputField.d.ts +19 -5
- package/dist/types/components/Form/components/fields/TextAreaInput.d.ts +12 -0
- package/dist/types/components/Form/components/{DatePicker.d.ts → fields/TimePicker.d.ts} +2 -2
- package/dist/types/components/Form/components/fields/types.d.ts +6 -0
- package/dist/types/components/Form/components/types/CustomJSONSchema7.d.ts +77 -4
- package/dist/types/components/Form/components/viewers/CustomViewer.d.ts +8 -0
- package/dist/types/components/Form/components/viewers/DateTimeViewer.d.ts +7 -0
- package/dist/types/components/Form/components/viewers/NumberViewer.d.ts +1 -1
- package/dist/types/components/Form/components/viewers/TextAreaViewer.d.ts +12 -0
- package/dist/types/components/Form/components/viewers/TimeViewer.d.ts +7 -0
- package/dist/types/components/Form/useForm.d.ts +6 -3
- package/dist/types/components/Form/utils/ajvResolver.d.ts +13 -0
- package/dist/types/components/Form/utils/buildErrorMessages.d.ts +223 -0
- package/dist/types/components/Form/utils/formatBytes.d.ts +6 -0
- package/dist/types/components/Form/utils/getFieldError.d.ts +6 -0
- package/dist/types/components/Form/utils/useFormI18n.d.ts +53 -0
- package/dist/types/components/Form/utils/validateData.d.ts +9 -0
- package/dist/types/components/TextArea/TextArea.d.ts +22 -0
- package/dist/types/components/TimePicker/TimePicker.d.ts +21 -0
- package/dist/types/components/ui/field.d.ts +3 -3
- package/dist/types/index.d.ts +19 -2
- package/package.json +18 -3
- package/dist/types/components/Controls/DensityFeature.d.ts +0 -23
- package/dist/types/components/Controls/DensityToggleButton.d.ts +0 -6
- package/dist/types/components/Controls/EditFilterButton.d.ts +0 -9
- package/dist/types/components/Controls/EditOrderButton.d.ts +0 -7
- package/dist/types/components/Controls/EditSortingButton.d.ts +0 -7
- package/dist/types/components/Controls/EditViewButton.d.ts +0 -7
- package/dist/types/components/Controls/FilterDialog.d.ts +0 -5
- package/dist/types/components/Controls/PageSizeControl.d.ts +0 -4
- package/dist/types/components/Controls/Pagination.d.ts +0 -1
- package/dist/types/components/Controls/ResetFilteringButton.d.ts +0 -4
- package/dist/types/components/Controls/ResetSelectionButton.d.ts +0 -4
- package/dist/types/components/Controls/ResetSortingButton.d.ts +0 -4
- package/dist/types/components/Controls/RowCountText.d.ts +0 -1
- package/dist/types/components/Controls/SelectAllRowsToggle.d.ts +0 -8
- package/dist/types/components/Controls/TablePagination.d.ts +0 -1
- package/dist/types/components/Controls/ViewDialog.d.ts +0 -5
- package/dist/types/components/DataTable/CardHeader.d.ts +0 -13
- package/dist/types/components/DataTable/DataDisplay.d.ts +0 -6
- package/dist/types/components/DataTable/ReloadButton.d.ts +0 -5
- package/dist/types/components/DataTable/Table.d.ts +0 -10
- package/dist/types/components/DataTable/TableBody.d.ts +0 -21
- package/dist/types/components/DataTable/TableCardContainer.d.ts +0 -7
- package/dist/types/components/DataTable/TableCards.d.ts +0 -11
- package/dist/types/components/DataTable/TableComponent.d.ts +0 -6
- package/dist/types/components/DataTable/TableControls.d.ts +0 -21
- package/dist/types/components/DataTable/TableFilter.d.ts +0 -1
- package/dist/types/components/DataTable/TableFilterTags.d.ts +0 -1
- package/dist/types/components/DataTable/TableFilters.d.ts +0 -1
- package/dist/types/components/DataTable/TableFooter.d.ts +0 -9
- package/dist/types/components/DataTable/TableHeader.d.ts +0 -13
- package/dist/types/components/DataTable/TableLoadingComponent.d.ts +0 -5
- package/dist/types/components/DataTable/TableOrderer.d.ts +0 -1
- package/dist/types/components/DataTable/TableSelector.d.ts +0 -1
- package/dist/types/components/DataTable/TableSorter.d.ts +0 -1
- package/dist/types/components/DataTable/TableViewer.d.ts +0 -1
- package/dist/types/components/DataTable/TextCell.d.ts +0 -10
- package/dist/types/components/DataTable/components/EmptyState.d.ts +0 -5
- package/dist/types/components/DataTable/components/ErrorAlert.d.ts +0 -4
- package/dist/types/components/DataTable/components/RecordDisplay.d.ts +0 -9
- package/dist/types/components/DataTable/components/TextCell.d.ts +0 -10
- package/dist/types/components/Filter/DateRangeFilter.d.ts +0 -9
- package/dist/types/components/Filter/FilterOptions.d.ts +0 -4
- package/dist/types/components/Form/Form.d.ts +0 -36
- package/dist/types/components/Form/components/ArrayRenderer.d.ts +0 -7
- package/dist/types/components/Form/components/BooleanPicker.d.ts +0 -7
- package/dist/types/components/Form/components/ColumnRenderer.d.ts +0 -7
- package/dist/types/components/Form/components/EnumPicker.d.ts +0 -8
- package/dist/types/components/Form/components/FilePicker.d.ts +0 -5
- package/dist/types/components/Form/components/IdPicker.d.ts +0 -8
- package/dist/types/components/Form/components/IdViewer.d.ts +0 -5
- package/dist/types/components/Form/components/NumberInputField.d.ts +0 -7
- package/dist/types/components/Form/components/ObjectInput.d.ts +0 -7
- package/dist/types/components/Form/components/RecordInput.d.ts +0 -7
- package/dist/types/components/Form/components/SchemaRenderer.d.ts +0 -7
- package/dist/types/components/Form/components/StringInputField.d.ts +0 -20
- package/dist/types/components/Form/components/TagPicker.d.ts +0 -30
package/dist/index.js
CHANGED
|
@@ -29,7 +29,11 @@ var gr = require('react-icons/gr');
|
|
|
29
29
|
var reactI18next = require('react-i18next');
|
|
30
30
|
var axios = require('axios');
|
|
31
31
|
var reactHookForm = require('react-hook-form');
|
|
32
|
+
var Ajv = require('ajv');
|
|
33
|
+
var addFormats = require('ajv-formats');
|
|
32
34
|
var dayjs = require('dayjs');
|
|
35
|
+
var utc = require('dayjs/plugin/utc');
|
|
36
|
+
var timezone = require('dayjs/plugin/timezone');
|
|
33
37
|
var ti = require('react-icons/ti');
|
|
34
38
|
|
|
35
39
|
function _interopNamespaceDefault(e) {
|
|
@@ -57,6 +61,56 @@ const DataTableContext = React.createContext({
|
|
|
57
61
|
setGlobalFilter: () => { },
|
|
58
62
|
type: "client",
|
|
59
63
|
translate: {},
|
|
64
|
+
data: [],
|
|
65
|
+
columns: [],
|
|
66
|
+
columnOrder: [],
|
|
67
|
+
columnFilters: [],
|
|
68
|
+
density: "sm",
|
|
69
|
+
sorting: [],
|
|
70
|
+
setPagination: function () {
|
|
71
|
+
throw new Error("Function not implemented.");
|
|
72
|
+
},
|
|
73
|
+
setSorting: function () {
|
|
74
|
+
throw new Error("Function not implemented.");
|
|
75
|
+
},
|
|
76
|
+
setColumnFilters: function () {
|
|
77
|
+
throw new Error("Function not implemented.");
|
|
78
|
+
},
|
|
79
|
+
setRowSelection: function () {
|
|
80
|
+
throw new Error("Function not implemented.");
|
|
81
|
+
},
|
|
82
|
+
setColumnOrder: function () {
|
|
83
|
+
throw new Error("Function not implemented.");
|
|
84
|
+
},
|
|
85
|
+
setDensity: function () {
|
|
86
|
+
throw new Error("Function not implemented.");
|
|
87
|
+
},
|
|
88
|
+
setColumnVisibility: function () {
|
|
89
|
+
throw new Error("Function not implemented.");
|
|
90
|
+
},
|
|
91
|
+
pagination: {
|
|
92
|
+
pageIndex: 0,
|
|
93
|
+
pageSize: 10,
|
|
94
|
+
},
|
|
95
|
+
rowSelection: {},
|
|
96
|
+
columnVisibility: {},
|
|
97
|
+
tableLabel: {
|
|
98
|
+
view: "View",
|
|
99
|
+
edit: "Edit",
|
|
100
|
+
filterButtonText: "Filter",
|
|
101
|
+
filterTitle: "Filter",
|
|
102
|
+
filterReset: "Reset",
|
|
103
|
+
filterClose: "Close",
|
|
104
|
+
reloadTooltip: "Reload",
|
|
105
|
+
reloadButtonText: "Reload",
|
|
106
|
+
resetSelection: "Reset Selection",
|
|
107
|
+
resetSorting: "Reset Sorting",
|
|
108
|
+
rowCountText: "Row Count",
|
|
109
|
+
hasErrorText: "Has Error",
|
|
110
|
+
globalFilterPlaceholder: "Search",
|
|
111
|
+
trueLabel: "True",
|
|
112
|
+
falseLabel: "False",
|
|
113
|
+
},
|
|
60
114
|
});
|
|
61
115
|
|
|
62
116
|
const useDataTableContext = () => {
|
|
@@ -112,11 +166,13 @@ const TableSorter = () => {
|
|
|
112
166
|
}) }))) }));
|
|
113
167
|
};
|
|
114
168
|
|
|
115
|
-
const ResetSortingButton = (
|
|
169
|
+
const ResetSortingButton = () => {
|
|
116
170
|
const { table } = useDataTableContext();
|
|
171
|
+
const { tableLabel } = useDataTableContext();
|
|
172
|
+
const { resetSorting } = tableLabel;
|
|
117
173
|
return (jsxRuntime.jsx(react.Button, { onClick: () => {
|
|
118
174
|
table.resetSorting();
|
|
119
|
-
}, children:
|
|
175
|
+
}, children: resetSorting }));
|
|
120
176
|
};
|
|
121
177
|
|
|
122
178
|
const EditSortingButton = ({ text, icon = jsxRuntime.jsx(md.MdOutlineSort, {}), title = "Edit Sorting", }) => {
|
|
@@ -144,7 +200,7 @@ const monthNamesFull = [
|
|
|
144
200
|
"November",
|
|
145
201
|
"December",
|
|
146
202
|
];
|
|
147
|
-
const weekdayNamesShort
|
|
203
|
+
const weekdayNamesShort = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
148
204
|
function Calendar$1({ calendars, getBackProps, getForwardProps, getDateProps, selected = [], firstDayOfWeek = 0, }) {
|
|
149
205
|
const [hoveredDate, setHoveredDate] = React.useState();
|
|
150
206
|
const onMouseLeave = () => {
|
|
@@ -179,7 +235,7 @@ function Calendar$1({ calendars, getBackProps, getForwardProps, getDateProps, se
|
|
|
179
235
|
offset: 12,
|
|
180
236
|
}), children: ">>" })] }), jsxRuntime.jsx(react.Grid, { templateColumns: "repeat(2, auto)", justifyContent: "center", gap: 4, children: calendars.map((calendar) => (jsxRuntime.jsxs(react.Grid, { gap: 4, children: [jsxRuntime.jsxs(react.Grid, { justifyContent: "center", children: [monthNamesFull[calendar.month], " ", calendar.year] }), jsxRuntime.jsx(react.Grid, { templateColumns: "repeat(7, auto)", justifyContent: "center", children: [0, 1, 2, 3, 4, 5, 6].map((weekdayNum) => {
|
|
181
237
|
const weekday = (weekdayNum + firstDayOfWeek) % 7;
|
|
182
|
-
return (jsxRuntime.jsx(react.Box, { minWidth: "48px", textAlign: "center", children: weekdayNamesShort
|
|
238
|
+
return (jsxRuntime.jsx(react.Box, { minWidth: "48px", textAlign: "center", children: weekdayNamesShort[weekday] }, `${calendar.month}${calendar.year}${weekday}`));
|
|
183
239
|
}) }), jsxRuntime.jsx(react.Grid, { templateColumns: "repeat(7, auto)", justifyContent: "center", children: calendar.weeks.map((week, windex) => week.map((dateObj, index) => {
|
|
184
240
|
const key = `${calendar.month}${calendar.year}${windex}${index}`;
|
|
185
241
|
if (!dateObj) {
|
|
@@ -300,8 +356,17 @@ const Tag = React__namespace.forwardRef(function Tag(props, ref) {
|
|
|
300
356
|
return (jsxRuntime.jsxs(react.Tag.Root, { ref: ref, ...rest, children: [startElement && (jsxRuntime.jsx(react.Tag.StartElement, { children: startElement })), jsxRuntime.jsx(react.Tag.Label, { children: children }), endElement && (jsxRuntime.jsx(react.Tag.EndElement, { children: endElement })), closable && (jsxRuntime.jsx(react.Tag.EndElement, { children: jsxRuntime.jsx(react.Tag.CloseTrigger, { onClick: onClose }) }))] }));
|
|
301
357
|
});
|
|
302
358
|
|
|
303
|
-
const TagFilter = ({ availableTags, selectedTags, onTagChange, }) => {
|
|
359
|
+
const TagFilter = ({ availableTags, selectedTags, onTagChange, selectOne = false, }) => {
|
|
304
360
|
const toggleTag = (tag) => {
|
|
361
|
+
if (selectOne) {
|
|
362
|
+
if (selectedTags.includes(tag)) {
|
|
363
|
+
onTagChange([]);
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
onTagChange([tag]);
|
|
367
|
+
}
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
305
370
|
if (selectedTags.includes(tag)) {
|
|
306
371
|
onTagChange(selectedTags.filter((t) => t !== tag));
|
|
307
372
|
}
|
|
@@ -309,10 +374,14 @@ const TagFilter = ({ availableTags, selectedTags, onTagChange, }) => {
|
|
|
309
374
|
onTagChange([...selectedTags, tag]);
|
|
310
375
|
}
|
|
311
376
|
};
|
|
312
|
-
return (jsxRuntime.jsx(react.Flex, { flexFlow: "wrap", p: "0.5rem", gap: "0.5rem", children: availableTags.map((tag) =>
|
|
377
|
+
return (jsxRuntime.jsx(react.Flex, { flexFlow: "wrap", p: "0.5rem", gap: "0.5rem", children: availableTags.map((tag) => {
|
|
378
|
+
const { label, value } = tag;
|
|
379
|
+
return (jsxRuntime.jsx(Tag, { variant: selectedTags.includes(value) ? "solid" : "outline", cursor: "pointer", closable: selectedTags.includes(value) ? true : undefined, onClick: () => toggleTag(value), children: label ?? value }));
|
|
380
|
+
}) }));
|
|
313
381
|
};
|
|
314
382
|
|
|
315
383
|
const Filter = ({ column }) => {
|
|
384
|
+
const { tableLabel } = useDataTableContext();
|
|
316
385
|
const { filterVariant } = column.columnDef.meta ?? {};
|
|
317
386
|
const displayName = column.columnDef.meta?.displayName ?? column.id;
|
|
318
387
|
const filterOptions = column.columnDef.meta?.filterOptions ?? [];
|
|
@@ -327,10 +396,14 @@ const Filter = ({ column }) => {
|
|
|
327
396
|
if (filterVariant === "select") {
|
|
328
397
|
return (jsxRuntime.jsxs(react.Flex, { flexFlow: "column", gap: "0.25rem", children: [jsxRuntime.jsx(react.Text, { children: displayName }), jsxRuntime.jsx(RadioGroup, { value: column.getFilterValue() ? String(column.getFilterValue()) : "", onValueChange: (details) => {
|
|
329
398
|
column.setFilterValue(details.value);
|
|
330
|
-
}, children: jsxRuntime.
|
|
399
|
+
}, children: jsxRuntime.jsxs(react.Flex, { flexFlow: "wrap", gap: "0.5rem", children: [filterOptions.length === 0 && jsxRuntime.jsx(react.Text, { children: "No filter options" }), filterOptions.length > 0 &&
|
|
400
|
+
filterOptions.map((item) => (jsxRuntime.jsx(Radio, { value: item.value, children: item.label }, item.value)))] }) })] }, column.id));
|
|
331
401
|
}
|
|
332
402
|
if (filterVariant === "tag") {
|
|
333
|
-
return (jsxRuntime.jsxs(react.Flex, { flexFlow: "column", gap: "0.25rem", children: [jsxRuntime.jsx(react.Text, { children: displayName }), jsxRuntime.jsx(TagFilter, { availableTags: filterOptions
|
|
403
|
+
return (jsxRuntime.jsxs(react.Flex, { flexFlow: "column", gap: "0.25rem", children: [jsxRuntime.jsx(react.Text, { children: displayName }), jsxRuntime.jsx(TagFilter, { availableTags: filterOptions.map((item) => ({
|
|
404
|
+
label: item.label,
|
|
405
|
+
value: item.value,
|
|
406
|
+
})), selectedTags: (column.getFilterValue() ?? []), onTagChange: (tags) => {
|
|
334
407
|
if (tags.length === 0) {
|
|
335
408
|
return column.setFilterValue(undefined);
|
|
336
409
|
}
|
|
@@ -338,7 +411,11 @@ const Filter = ({ column }) => {
|
|
|
338
411
|
} })] }, column.id));
|
|
339
412
|
}
|
|
340
413
|
if (filterVariant === "boolean") {
|
|
341
|
-
|
|
414
|
+
const { trueLabel, falseLabel } = tableLabel;
|
|
415
|
+
return (jsxRuntime.jsxs(react.Flex, { flexFlow: "column", gap: "0.25rem", children: [jsxRuntime.jsx(react.Text, { children: displayName }), jsxRuntime.jsx(TagFilter, { availableTags: [
|
|
416
|
+
{ label: trueLabel, value: "true" },
|
|
417
|
+
{ label: falseLabel, value: "false" },
|
|
418
|
+
], selectedTags: (column.getFilterValue() ?? []), onTagChange: (tags) => {
|
|
342
419
|
if (tags.length === 0) {
|
|
343
420
|
return column.setFilterValue(undefined);
|
|
344
421
|
}
|
|
@@ -393,17 +470,20 @@ const TableFilter = () => {
|
|
|
393
470
|
}) }));
|
|
394
471
|
};
|
|
395
472
|
|
|
396
|
-
const ResetFilteringButton = (
|
|
473
|
+
const ResetFilteringButton = () => {
|
|
397
474
|
const { table } = useDataTableContext();
|
|
475
|
+
const { tableLabel } = useDataTableContext();
|
|
476
|
+
const { filterReset } = tableLabel;
|
|
398
477
|
return (jsxRuntime.jsx(react.Button, { onClick: () => {
|
|
399
478
|
table.resetColumnFilters();
|
|
400
|
-
}, children:
|
|
479
|
+
}, children: filterReset }));
|
|
401
480
|
};
|
|
402
481
|
|
|
403
482
|
const FilterDialog = ({ icon = jsxRuntime.jsx(md.MdFilterAlt, {}), }) => {
|
|
404
483
|
const filterModal = react.useDisclosure();
|
|
405
|
-
const {
|
|
406
|
-
|
|
484
|
+
const { tableLabel } = useDataTableContext();
|
|
485
|
+
const { filterButtonText, filterTitle, filterClose } = tableLabel;
|
|
486
|
+
return (jsxRuntime.jsxs(DialogRoot, { size: ["full", "full", "md", "md"], open: filterModal.open, children: [jsxRuntime.jsx(DialogTrigger, { asChild: true, children: jsxRuntime.jsxs(react.Button, { as: react.Box, variant: "ghost", onClick: filterModal.onOpen, children: [icon, " ", filterButtonText] }) }), jsxRuntime.jsxs(DialogContent, { children: [jsxRuntime.jsx(DialogHeader, { children: jsxRuntime.jsx(DialogTitle, { children: filterTitle }) }), jsxRuntime.jsx(DialogBody, { display: "flex", flexFlow: "column", children: jsxRuntime.jsx(TableFilter, {}) }), jsxRuntime.jsxs(DialogFooter, { children: [jsxRuntime.jsx(ResetFilteringButton, {}), jsxRuntime.jsx(react.Button, { onClick: filterModal.onClose, variant: "subtle", children: filterClose })] }), jsxRuntime.jsx(DialogCloseTrigger, { onClick: filterModal.onClose })] })] }));
|
|
407
487
|
};
|
|
408
488
|
|
|
409
489
|
const MenuContent = React__namespace.forwardRef(function MenuContent(props, ref) {
|
|
@@ -522,11 +602,13 @@ const Pagination = () => {
|
|
|
522
602
|
}, children: jsxRuntime.jsxs(react.HStack, { children: [jsxRuntime.jsx(PaginationPrevTrigger, {}), jsxRuntime.jsx(PaginationItems, {}), jsxRuntime.jsx(PaginationNextTrigger, {})] }) }));
|
|
523
603
|
};
|
|
524
604
|
|
|
525
|
-
const ResetSelectionButton = (
|
|
605
|
+
const ResetSelectionButton = () => {
|
|
526
606
|
const { table } = useDataTableContext();
|
|
607
|
+
const { tableLabel } = useDataTableContext();
|
|
608
|
+
const { resetSelection } = tableLabel;
|
|
527
609
|
return (jsxRuntime.jsx(react.Button, { onClick: () => {
|
|
528
610
|
table.resetRowSelection();
|
|
529
|
-
}, children:
|
|
611
|
+
}, children: resetSelection }));
|
|
530
612
|
};
|
|
531
613
|
|
|
532
614
|
const RowCountText = () => {
|
|
@@ -2441,8 +2523,8 @@ react.CheckboxCard.Indicator;
|
|
|
2441
2523
|
function ColumnCard({ columnId }) {
|
|
2442
2524
|
const ref = React.useRef(null);
|
|
2443
2525
|
const [dragging, setDragging] = React.useState(false); // NEW
|
|
2444
|
-
const { table } = useDataTableContext();
|
|
2445
|
-
const displayName = columnId;
|
|
2526
|
+
const { table, translate } = useDataTableContext();
|
|
2527
|
+
const displayName = translate.t(columnId);
|
|
2446
2528
|
const column = table.getColumn(columnId);
|
|
2447
2529
|
invariant(column);
|
|
2448
2530
|
React.useEffect(() => {
|
|
@@ -2457,7 +2539,7 @@ function ColumnCard({ columnId }) {
|
|
|
2457
2539
|
onDrop: () => setDragging(false), // NEW
|
|
2458
2540
|
});
|
|
2459
2541
|
}, [columnId, table]);
|
|
2460
|
-
return (jsxRuntime.jsxs(react.Grid, { ref: ref, templateColumns: "auto 1fr", gap: "0.5rem", alignItems: "center", style: dragging ? { opacity: 0.4 } : {}, children: [jsxRuntime.jsx(react.Flex, { alignItems: "center", padding: "0", cursor: "grab", children: jsxRuntime.jsx(fa6.FaGripLinesVertical, { color: "
|
|
2542
|
+
return (jsxRuntime.jsxs(react.Grid, { ref: ref, templateColumns: "auto 1fr", gap: "0.5rem", alignItems: "center", style: dragging ? { opacity: 0.4 } : {}, children: [jsxRuntime.jsx(react.Flex, { alignItems: "center", padding: "0", cursor: "grab", children: jsxRuntime.jsx(fa6.FaGripLinesVertical, { color: "colorPalette.400" }) }), jsxRuntime.jsx(react.Flex, { justifyContent: "space-between", alignItems: "center", children: jsxRuntime.jsx(CheckboxCard, { variant: "surface", label: displayName, checked: column.getIsVisible(), onChange: column.getToggleVisibilityHandler() }) })] }));
|
|
2461
2543
|
}
|
|
2462
2544
|
function CardContainer({ location, children }) {
|
|
2463
2545
|
const ref = React.useRef(null);
|
|
@@ -2476,7 +2558,6 @@ function CardContainer({ location, children }) {
|
|
|
2476
2558
|
onDrop: () => setIsDraggedOver(false),
|
|
2477
2559
|
});
|
|
2478
2560
|
}, [location]);
|
|
2479
|
-
// const isDark = (location + location) % 2 === 1;
|
|
2480
2561
|
function getColor(isDraggedOver) {
|
|
2481
2562
|
if (isDraggedOver) {
|
|
2482
2563
|
return {
|
|
@@ -2486,7 +2567,6 @@ function CardContainer({ location, children }) {
|
|
|
2486
2567
|
},
|
|
2487
2568
|
};
|
|
2488
2569
|
}
|
|
2489
|
-
// return isDark ? "lightgrey" : "white";
|
|
2490
2570
|
return {
|
|
2491
2571
|
backgroundColor: undefined,
|
|
2492
2572
|
_dark: {
|
|
@@ -2537,8 +2617,9 @@ const TableViewer = () => {
|
|
|
2537
2617
|
|
|
2538
2618
|
const ViewDialog = ({ icon = jsxRuntime.jsx(io.IoMdEye, {}) }) => {
|
|
2539
2619
|
const viewModel = react.useDisclosure();
|
|
2540
|
-
const {
|
|
2541
|
-
|
|
2620
|
+
const { tableLabel } = useDataTableContext();
|
|
2621
|
+
const { view } = tableLabel;
|
|
2622
|
+
return (jsxRuntime.jsxs(DialogRoot, { children: [jsxRuntime.jsx(react.DialogBackdrop, {}), jsxRuntime.jsx(DialogTrigger, { asChild: true, children: jsxRuntime.jsxs(react.Button, { as: react.Box, variant: "ghost", onClick: viewModel.onOpen, children: [icon, " ", view] }) }), jsxRuntime.jsxs(DialogContent, { children: [jsxRuntime.jsx(DialogCloseTrigger, {}), jsxRuntime.jsx(DialogHeader, { children: jsxRuntime.jsx(DialogTitle, { children: view }) }), jsxRuntime.jsx(DialogBody, { children: jsxRuntime.jsx(TableViewer, {}) }), jsxRuntime.jsx(DialogFooter, {})] })] }));
|
|
2542
2623
|
};
|
|
2543
2624
|
|
|
2544
2625
|
const CardHeader = ({ row, imageColumnId = undefined, titleColumnId = undefined, tagColumnId = undefined, tagIcon = undefined, showTag = true, imageProps = {}, }) => {
|
|
@@ -2589,7 +2670,7 @@ const RecordDisplay = ({ object, boxProps, translate, prefix = "", }) => {
|
|
|
2589
2670
|
return jsxRuntime.jsx(jsxRuntime.Fragment, { children: "null" });
|
|
2590
2671
|
}
|
|
2591
2672
|
return (jsxRuntime.jsx(react.Grid, { rowGap: 1, padding: 1, overflow: "auto", ...boxProps, children: Object.entries(object).map(([field, value]) => {
|
|
2592
|
-
return (jsxRuntime.jsxs(react.Grid, { columnGap: 2, gridTemplateColumns: "auto 1fr", children: [jsxRuntime.jsx(react.Text, { color: "
|
|
2673
|
+
return (jsxRuntime.jsxs(react.Grid, { columnGap: 2, gridTemplateColumns: "auto 1fr", children: [jsxRuntime.jsx(react.Text, { color: "colorPalette.400", children: getColumn({ field }) }), typeof value === "object" ? (jsxRuntime.jsx(RecordDisplay, { object: value, prefix: `${prefix}${field}.`, translate: translate })) : (jsxRuntime.jsx(react.Text, { children: JSON.stringify(value) }))] }, field));
|
|
2593
2674
|
}) }));
|
|
2594
2675
|
};
|
|
2595
2676
|
|
|
@@ -2639,7 +2720,7 @@ const CellRenderer = ({ cell }) => {
|
|
|
2639
2720
|
paddingY: 2,
|
|
2640
2721
|
}, object: value })] }, cell.id));
|
|
2641
2722
|
}
|
|
2642
|
-
return (jsxRuntime.jsxs(react.Box, { gridColumn, gridRow, children: [jsxRuntime.jsx(react.Box, { color:
|
|
2723
|
+
return (jsxRuntime.jsxs(react.Box, { gridColumn, gridRow, children: [jsxRuntime.jsx(react.Box, { color: "colorPalette.400", children: getLabel({ columnId: cell.column.id }) }), jsxRuntime.jsx(react.Box, { wordBreak: "break-word", textOverflow: "ellipsis", overflow: "hidden", children: `${formatValue(cell.getValue())}` })] }, cell.id));
|
|
2643
2724
|
};
|
|
2644
2725
|
const DataDisplay = ({ variant = "" }) => {
|
|
2645
2726
|
const { table, translate } = useDataTableContext();
|
|
@@ -2761,7 +2842,23 @@ const fuzzyFilter = (row, columnId, value, addMeta) => {
|
|
|
2761
2842
|
*
|
|
2762
2843
|
* @link https://tanstack.com/table/latest/docs/guide/column-defs
|
|
2763
2844
|
*/
|
|
2764
|
-
function DataTable({ columns, data, enableRowSelection = true, enableMultiRowSelection = true, enableSubRowSelection = true, columnOrder, columnFilters, columnVisibility, density, globalFilter, pagination, sorting, rowSelection, setPagination, setSorting, setColumnFilters, setRowSelection, setGlobalFilter, setColumnOrder, setDensity, setColumnVisibility, translate, children,
|
|
2845
|
+
function DataTable({ columns, data, enableRowSelection = true, enableMultiRowSelection = true, enableSubRowSelection = true, columnOrder, columnFilters, columnVisibility, density, globalFilter, pagination, sorting, rowSelection, setPagination, setSorting, setColumnFilters, setRowSelection, setGlobalFilter, setColumnOrder, setDensity, setColumnVisibility, translate, children, tableLabel = {
|
|
2846
|
+
view: 'View',
|
|
2847
|
+
edit: 'Edit',
|
|
2848
|
+
filterButtonText: 'Filter',
|
|
2849
|
+
filterTitle: 'Filter',
|
|
2850
|
+
filterReset: 'Reset',
|
|
2851
|
+
filterClose: 'Close',
|
|
2852
|
+
reloadTooltip: 'Reload',
|
|
2853
|
+
reloadButtonText: 'Reload',
|
|
2854
|
+
resetSelection: 'Reset Selection',
|
|
2855
|
+
resetSorting: 'Reset Sorting',
|
|
2856
|
+
rowCountText: 'Row Count',
|
|
2857
|
+
hasErrorText: 'Has Error',
|
|
2858
|
+
globalFilterPlaceholder: 'Search',
|
|
2859
|
+
trueLabel: 'True',
|
|
2860
|
+
falseLabel: 'False',
|
|
2861
|
+
}, }) {
|
|
2765
2862
|
const table = reactTable.useReactTable({
|
|
2766
2863
|
_features: [DensityFeature],
|
|
2767
2864
|
data: data,
|
|
@@ -2779,12 +2876,12 @@ function DataTable({ columns, data, enableRowSelection = true, enableMultiRowSel
|
|
|
2779
2876
|
enableRowSelection: enableRowSelection,
|
|
2780
2877
|
enableMultiRowSelection: enableMultiRowSelection,
|
|
2781
2878
|
enableSubRowSelection: enableSubRowSelection,
|
|
2782
|
-
columnResizeMode:
|
|
2879
|
+
columnResizeMode: 'onChange',
|
|
2783
2880
|
// global filter start
|
|
2784
2881
|
filterFns: {
|
|
2785
2882
|
fuzzy: fuzzyFilter,
|
|
2786
2883
|
},
|
|
2787
|
-
globalFilterFn:
|
|
2884
|
+
globalFilterFn: 'fuzzy',
|
|
2788
2885
|
state: {
|
|
2789
2886
|
pagination,
|
|
2790
2887
|
sorting,
|
|
@@ -2812,9 +2909,9 @@ function DataTable({ columns, data, enableRowSelection = true, enableMultiRowSel
|
|
|
2812
2909
|
table: table,
|
|
2813
2910
|
globalFilter,
|
|
2814
2911
|
setGlobalFilter,
|
|
2815
|
-
type:
|
|
2912
|
+
type: 'client',
|
|
2816
2913
|
translate,
|
|
2817
|
-
columns,
|
|
2914
|
+
columns: columns,
|
|
2818
2915
|
sorting,
|
|
2819
2916
|
setSorting,
|
|
2820
2917
|
columnFilters,
|
|
@@ -2829,6 +2926,8 @@ function DataTable({ columns, data, enableRowSelection = true, enableMultiRowSel
|
|
|
2829
2926
|
setDensity,
|
|
2830
2927
|
columnVisibility,
|
|
2831
2928
|
setColumnVisibility,
|
|
2929
|
+
data,
|
|
2930
|
+
tableLabel,
|
|
2832
2931
|
}, children: children }));
|
|
2833
2932
|
}
|
|
2834
2933
|
|
|
@@ -2843,10 +2942,26 @@ function DataTable({ columns, data, enableRowSelection = true, enableMultiRowSel
|
|
|
2843
2942
|
*
|
|
2844
2943
|
* @link https://tanstack.com/table/latest/docs/guide/column-defs
|
|
2845
2944
|
*/
|
|
2846
|
-
function DataTableServer({ columns, enableRowSelection = true, enableMultiRowSelection = true, enableSubRowSelection = true, columnOrder, columnFilters, columnVisibility, density, globalFilter, pagination, sorting, rowSelection, setPagination, setSorting, setColumnFilters, setRowSelection, setGlobalFilter, setColumnOrder, setDensity, setColumnVisibility, query, url, translate, children,
|
|
2945
|
+
function DataTableServer({ columns, enableRowSelection = true, enableMultiRowSelection = true, enableSubRowSelection = true, columnOrder, columnFilters, columnVisibility, density, globalFilter, pagination, sorting, rowSelection, setPagination, setSorting, setColumnFilters, setRowSelection, setGlobalFilter, setColumnOrder, setDensity, setColumnVisibility, query, url, translate, children, tableLabel = {
|
|
2946
|
+
view: "View",
|
|
2947
|
+
edit: "Edit",
|
|
2948
|
+
filterButtonText: "Filter",
|
|
2949
|
+
filterTitle: "Filter",
|
|
2950
|
+
filterReset: "Reset",
|
|
2951
|
+
filterClose: "Close",
|
|
2952
|
+
reloadTooltip: "Reload",
|
|
2953
|
+
reloadButtonText: "Reload",
|
|
2954
|
+
resetSelection: "Reset Selection",
|
|
2955
|
+
resetSorting: "Reset Sorting",
|
|
2956
|
+
rowCountText: "Row Count",
|
|
2957
|
+
hasErrorText: "Has Error",
|
|
2958
|
+
globalFilterPlaceholder: "Search",
|
|
2959
|
+
trueLabel: "True",
|
|
2960
|
+
falseLabel: "False",
|
|
2961
|
+
}, }) {
|
|
2847
2962
|
const table = reactTable.useReactTable({
|
|
2848
2963
|
_features: [DensityFeature],
|
|
2849
|
-
data: query.data?.data ?? [],
|
|
2964
|
+
data: (query.data?.data ?? []),
|
|
2850
2965
|
rowCount: query.data?.count ?? 0,
|
|
2851
2966
|
columns: columns,
|
|
2852
2967
|
getCoreRowModel: reactTable.getCoreRowModel(),
|
|
@@ -2892,12 +3007,12 @@ function DataTableServer({ columns, enableRowSelection = true, enableMultiRowSel
|
|
|
2892
3007
|
// for tanstack-table ts bug end
|
|
2893
3008
|
});
|
|
2894
3009
|
return (jsxRuntime.jsx(DataTableContext.Provider, { value: {
|
|
2895
|
-
table:
|
|
3010
|
+
table: table,
|
|
2896
3011
|
globalFilter,
|
|
2897
3012
|
setGlobalFilter,
|
|
2898
3013
|
type: "server",
|
|
2899
3014
|
translate,
|
|
2900
|
-
columns,
|
|
3015
|
+
columns: columns,
|
|
2901
3016
|
sorting,
|
|
2902
3017
|
setSorting,
|
|
2903
3018
|
columnFilters,
|
|
@@ -2912,15 +3027,108 @@ function DataTableServer({ columns, enableRowSelection = true, enableMultiRowSel
|
|
|
2912
3027
|
setDensity,
|
|
2913
3028
|
columnVisibility,
|
|
2914
3029
|
setColumnVisibility,
|
|
3030
|
+
data: query.data?.data ?? [],
|
|
3031
|
+
tableLabel,
|
|
2915
3032
|
}, children: jsxRuntime.jsx(DataTableServerContext.Provider, { value: { url, query }, children: children }) }));
|
|
2916
3033
|
}
|
|
2917
3034
|
|
|
3035
|
+
const InputGroup = React__namespace.forwardRef(function InputGroup(props, ref) {
|
|
3036
|
+
const { startElement, startElementProps, endElement, endElementProps, children, startOffset = "6px", endOffset = "6px", ...rest } = props;
|
|
3037
|
+
return (jsxRuntime.jsxs(react.Group, { ref: ref, ...rest, children: [startElement && (jsxRuntime.jsx(react.InputElement, { pointerEvents: "none", ...startElementProps, children: startElement })), React__namespace.cloneElement(children, {
|
|
3038
|
+
...(startElement && {
|
|
3039
|
+
ps: `calc(var(--input-height) - ${startOffset})`,
|
|
3040
|
+
}),
|
|
3041
|
+
...(endElement && { pe: `calc(var(--input-height) - ${endOffset})` }),
|
|
3042
|
+
// @ts-expect-error chakra generated files
|
|
3043
|
+
...children.props,
|
|
3044
|
+
}), endElement && (jsxRuntime.jsx(react.InputElement, { placement: "end", ...endElementProps, children: endElement }))] }));
|
|
3045
|
+
});
|
|
3046
|
+
|
|
3047
|
+
const GlobalFilter = () => {
|
|
3048
|
+
const { table, tableLabel } = useDataTableContext();
|
|
3049
|
+
const { globalFilterPlaceholder } = tableLabel;
|
|
3050
|
+
const [searchTerm, setSearchTerm] = React.useState("");
|
|
3051
|
+
const debouncedSearchTerm = usehooks.useDebounce(searchTerm, 500);
|
|
3052
|
+
React.useEffect(() => {
|
|
3053
|
+
const searchHN = async () => {
|
|
3054
|
+
table.setGlobalFilter(debouncedSearchTerm);
|
|
3055
|
+
};
|
|
3056
|
+
searchHN();
|
|
3057
|
+
}, [debouncedSearchTerm]);
|
|
3058
|
+
return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsx(InputGroup, { flex: "1", startElement: jsxRuntime.jsx(md.MdSearch, {}), children: jsxRuntime.jsx(react.Input, { placeholder: globalFilterPlaceholder, variant: "outline", onChange: (e) => {
|
|
3059
|
+
setSearchTerm(e.target.value);
|
|
3060
|
+
} }) }) }));
|
|
3061
|
+
};
|
|
3062
|
+
|
|
3063
|
+
const Tooltip = React__namespace.forwardRef(function Tooltip(props, ref) {
|
|
3064
|
+
const { showArrow, children, disabled, portalled, content, contentProps, portalRef, ...rest } = props;
|
|
3065
|
+
if (disabled)
|
|
3066
|
+
return children;
|
|
3067
|
+
return (jsxRuntime.jsxs(react.Tooltip.Root, { ...rest, children: [jsxRuntime.jsx(react.Tooltip.Trigger, { asChild: true, children: children }), jsxRuntime.jsx(react.Portal, { disabled: !portalled, container: portalRef, children: jsxRuntime.jsx(react.Tooltip.Positioner, { children: jsxRuntime.jsxs(react.Tooltip.Content, { ref: ref, ...contentProps, children: [showArrow && (jsxRuntime.jsx(react.Tooltip.Arrow, { children: jsxRuntime.jsx(react.Tooltip.ArrowTip, {}) })), content] }) }) })] }));
|
|
3068
|
+
});
|
|
3069
|
+
|
|
3070
|
+
const ReloadButton = ({ variant = "icon", }) => {
|
|
3071
|
+
const { url } = useDataTableServerContext();
|
|
3072
|
+
const queryClient = reactQuery.useQueryClient();
|
|
3073
|
+
const { tableLabel } = useDataTableContext();
|
|
3074
|
+
const { reloadTooltip, reloadButtonText } = tableLabel;
|
|
3075
|
+
if (variant === "icon") {
|
|
3076
|
+
return (jsxRuntime.jsx(Tooltip, { showArrow: true, content: reloadTooltip, children: jsxRuntime.jsx(Button, { variant: "ghost", onClick: () => {
|
|
3077
|
+
queryClient.invalidateQueries({ queryKey: [url] });
|
|
3078
|
+
}, "aria-label": "refresh", children: jsxRuntime.jsx(io5.IoReload, {}) }) }));
|
|
3079
|
+
}
|
|
3080
|
+
return (jsxRuntime.jsxs(Button, { variant: "ghost", onClick: () => {
|
|
3081
|
+
queryClient.invalidateQueries({ queryKey: [url] });
|
|
3082
|
+
}, children: [jsxRuntime.jsx(io5.IoReload, {}), " ", reloadButtonText] }));
|
|
3083
|
+
};
|
|
3084
|
+
|
|
3085
|
+
const TableFilterTags = () => {
|
|
3086
|
+
const { table } = useDataTableContext();
|
|
3087
|
+
return (jsxRuntime.jsx(react.Flex, { gap: "0.5rem", flexFlow: "wrap", children: table.getState().columnFilters.map(({ id, value }) => {
|
|
3088
|
+
return (jsxRuntime.jsx(Tag, { gap: "0.5rem", closable: true, cursor: "pointer", onClick: () => {
|
|
3089
|
+
table.setColumnFilters(table.getState().columnFilters.filter((filter) => {
|
|
3090
|
+
return filter.value != value;
|
|
3091
|
+
}));
|
|
3092
|
+
}, children: `${id}: ${value}` }, `${id}-${value}`));
|
|
3093
|
+
}) }));
|
|
3094
|
+
};
|
|
3095
|
+
|
|
3096
|
+
const TableControls = ({ fitTableWidth = false, fitTableHeight = false, children = jsxRuntime.jsx(jsxRuntime.Fragment, {}), showGlobalFilter = false, showFilter = false, showFilterName = false, showFilterTags = false, showReload = false, showPagination = true, showPageSizeControl = true, showPageCountText = true, showView = true, filterTagsOptions = [], extraItems = jsxRuntime.jsx(jsxRuntime.Fragment, {}), loading = false, hasError = false, gridProps = {}, }) => {
|
|
3097
|
+
const { tableLabel, table } = useDataTableContext();
|
|
3098
|
+
const { rowCountText, hasErrorText } = tableLabel;
|
|
3099
|
+
return (jsxRuntime.jsxs(react.Grid, { templateRows: "auto 1fr", width: fitTableWidth ? "fit-content" : "100%", height: fitTableHeight ? "fit-content" : "100%", gap: "0.5rem", ...gridProps, children: [jsxRuntime.jsxs(react.Flex, { flexFlow: "column", gap: 2, children: [jsxRuntime.jsxs(react.Flex, { justifyContent: "space-between", children: [jsxRuntime.jsx(react.Box, { children: showView && jsxRuntime.jsx(ViewDialog, { icon: jsxRuntime.jsx(md.MdOutlineViewColumn, {}) }) }), jsxRuntime.jsxs(react.Flex, { gap: "0.5rem", alignItems: "center", justifySelf: "end", children: [loading && jsxRuntime.jsx(react.Spinner, { size: "sm" }), hasError && (jsxRuntime.jsx(Tooltip, { content: hasErrorText, children: jsxRuntime.jsx(react.Icon, { as: bs.BsExclamationCircleFill, color: "red.400" }) })), showGlobalFilter && jsxRuntime.jsx(GlobalFilter, {}), showFilter && jsxRuntime.jsx(FilterDialog, {}), showReload && jsxRuntime.jsx(ReloadButton, {}), extraItems] })] }), filterTagsOptions.length > 0 && (jsxRuntime.jsx(react.Flex, { flexFlow: "column", gap: "0.5rem", children: filterTagsOptions.map((option) => {
|
|
3100
|
+
const { column, options } = option;
|
|
3101
|
+
const tableColumn = table.getColumn(column);
|
|
3102
|
+
return (jsxRuntime.jsxs(react.Flex, { alignItems: "center", flexFlow: "wrap", gap: "0.5rem", children: [tableColumn?.columnDef.meta?.displayName && (jsxRuntime.jsx(react.Text, { children: tableColumn?.columnDef.meta?.displayName })), jsxRuntime.jsx(TagFilter, { availableTags: options, selectedTags: tableColumn?.getFilterValue() ?? [], selectOne: true, onTagChange: (tags) => {
|
|
3103
|
+
if (tags.length === 0) {
|
|
3104
|
+
return tableColumn?.setFilterValue(undefined);
|
|
3105
|
+
}
|
|
3106
|
+
tableColumn?.setFilterValue(tags);
|
|
3107
|
+
} })] }, column));
|
|
3108
|
+
}) })), showFilterTags && (jsxRuntime.jsx(react.Flex, { children: jsxRuntime.jsx(TableFilterTags, {}) }))] }), jsxRuntime.jsx(react.Grid, { overflow: "auto", bg: { base: "colorPalette.50", _dark: "colorPalette.950" }, children: children }), (showPageSizeControl || showPageCountText || showPagination) && (jsxRuntime.jsxs(react.Flex, { justifyContent: "space-between", children: [jsxRuntime.jsxs(react.Flex, { gap: "1rem", alignItems: "center", children: [showPageSizeControl && jsxRuntime.jsx(PageSizeControl, {}), showPageCountText && (jsxRuntime.jsxs(react.Flex, { children: [jsxRuntime.jsx(react.Text, { paddingRight: "0.5rem", children: rowCountText }), jsxRuntime.jsx(RowCountText, {})] }))] }), jsxRuntime.jsx(react.Box, { justifySelf: "end", children: showPagination && jsxRuntime.jsx(Pagination, {}) })] }))] }));
|
|
3109
|
+
};
|
|
3110
|
+
|
|
3111
|
+
const EmptyState = React__namespace.forwardRef(function EmptyState(props, ref) {
|
|
3112
|
+
const { title, description, icon, children, ...rest } = props;
|
|
3113
|
+
return (jsxRuntime.jsx(react.EmptyState.Root, { ref: ref, ...rest, children: jsxRuntime.jsxs(react.EmptyState.Content, { children: [icon && (jsxRuntime.jsx(react.EmptyState.Indicator, { children: icon })), description ? (jsxRuntime.jsxs(react.VStack, { textAlign: "center", children: [jsxRuntime.jsx(react.EmptyState.Title, { children: title }), jsxRuntime.jsx(react.EmptyState.Description, { children: description })] })) : (jsxRuntime.jsx(react.EmptyState.Title, { children: title })), children] }) }));
|
|
3114
|
+
});
|
|
3115
|
+
|
|
3116
|
+
const EmptyResult = (jsxRuntime.jsx(EmptyState, { icon: jsxRuntime.jsx(hi.HiColorSwatch, {}), title: "No results found", description: "Try adjusting your search", children: jsxRuntime.jsxs(react.List.Root, { variant: "marker", children: [jsxRuntime.jsx(react.List.Item, { children: "Try removing filters" }), jsxRuntime.jsx(react.List.Item, { children: "Try different keywords" })] }) }));
|
|
3117
|
+
const Table = ({ children, emptyComponent = EmptyResult, canResize = true, showLoading = false, ...props }) => {
|
|
3118
|
+
const { table } = useDataTableContext();
|
|
3119
|
+
// Skip empty check when loading to allow skeleton to render
|
|
3120
|
+
if (!showLoading && table.getRowModel().rows.length <= 0) {
|
|
3121
|
+
return emptyComponent;
|
|
3122
|
+
}
|
|
3123
|
+
return (jsxRuntime.jsx(react.Table.Root, { stickyHeader: true, variant: 'outline', width: canResize ? table.getCenterTotalSize() : undefined, display: 'grid', alignContent: 'start', overflowY: 'auto', bg: { base: 'colorPalette.50', _dark: 'colorPalette.950' }, ...props, children: children }));
|
|
3124
|
+
};
|
|
3125
|
+
|
|
2918
3126
|
const Checkbox = React__namespace.forwardRef(function Checkbox(props, ref) {
|
|
2919
3127
|
const { icon, children, inputProps, rootRef, ...rest } = props;
|
|
2920
3128
|
return (jsxRuntime.jsxs(react.Checkbox.Root, { ref: rootRef, ...rest, children: [jsxRuntime.jsx(react.Checkbox.HiddenInput, { ref: ref, ...inputProps }), jsxRuntime.jsx(react.Checkbox.Control, { children: icon || jsxRuntime.jsx(react.Checkbox.Indicator, {}) }), children != null && (jsxRuntime.jsx(react.Checkbox.Label, { children: children }))] }));
|
|
2921
3129
|
});
|
|
2922
3130
|
|
|
2923
|
-
const TableBody = ({
|
|
3131
|
+
const TableBody = ({ showSelector = false, canResize = true, }) => {
|
|
2924
3132
|
"use no memo";
|
|
2925
3133
|
const { table } = useDataTableContext();
|
|
2926
3134
|
const SELECTION_BOX_WIDTH = 20;
|
|
@@ -2934,12 +3142,7 @@ const TableBody = ({ pinnedBgColor = { light: "gray.50", dark: "gray.700" }, sho
|
|
|
2934
3142
|
left: showSelector
|
|
2935
3143
|
? `${cell.column.getStart("left") + SELECTION_BOX_WIDTH + table.getDensityValue() * 2}px`
|
|
2936
3144
|
: `${cell.column.getStart("left")}px`,
|
|
2937
|
-
|
|
2938
|
-
position: "sticky",
|
|
2939
|
-
zIndex: -1,
|
|
2940
|
-
_dark: {
|
|
2941
|
-
backgroundColor: pinnedBgColor.dark,
|
|
2942
|
-
},
|
|
3145
|
+
position: "relative",
|
|
2943
3146
|
}
|
|
2944
3147
|
: {};
|
|
2945
3148
|
return tdProps;
|
|
@@ -2958,133 +3161,91 @@ const TableBody = ({ pinnedBgColor = { light: "gray.50", dark: "gray.700" }, sho
|
|
|
2958
3161
|
};
|
|
2959
3162
|
};
|
|
2960
3163
|
return (jsxRuntime.jsx(react.Table.Body, { children: table.getRowModel().rows.map((row, index) => {
|
|
2961
|
-
return (jsxRuntime.jsxs(react.Table.Row, { display: "flex", zIndex: 1, onMouseEnter: () => handleRowHover(index), onMouseLeave: () => handleRowHover(-1), ...getTrProps({ hoveredRow, index }), children: [showSelector && (jsxRuntime.jsx(TableRowSelector, { index: index, row: row, hoveredRow: hoveredRow
|
|
3164
|
+
return (jsxRuntime.jsxs(react.Table.Row, { display: "flex", zIndex: 1, onMouseEnter: () => handleRowHover(index), onMouseLeave: () => handleRowHover(-1), ...getTrProps({ hoveredRow, index }), children: [showSelector && (jsxRuntime.jsx(TableRowSelector, { index: index, row: row, hoveredRow: hoveredRow })), row.getVisibleCells().map((cell, index) => {
|
|
2962
3165
|
return (jsxRuntime.jsx(react.Table.Cell, { padding: `${table.getDensityValue()}px`,
|
|
2963
3166
|
// styling resize and pinning start
|
|
2964
|
-
flex: `${canResize ? "0" : "1"} 0 ${cell.column.getSize()}px`,
|
|
2965
|
-
|
|
2966
|
-
|
|
3167
|
+
flex: `${canResize ? "0" : "1"} 0 ${cell.column.getSize()}px`,
|
|
3168
|
+
// this is to avoid the cell from being too wide
|
|
3169
|
+
minWidth: `0`, color: {
|
|
3170
|
+
base: "colorPalette.900",
|
|
3171
|
+
_dark: "colorPalette.100",
|
|
3172
|
+
},
|
|
3173
|
+
bg: { base: "colorPalette.50", _dark: "colorPalette.950" }, ...getTdProps(cell), children: reactTable.flexRender(cell.column.columnDef.cell, cell.getContext()) }, `chakra-table-rowcell-${cell.id}-${index}`));
|
|
2967
3174
|
})] }, `chakra-table-row-${row.id}`));
|
|
2968
3175
|
}) }));
|
|
2969
3176
|
};
|
|
2970
|
-
const TableRowSelector = ({
|
|
3177
|
+
const TableRowSelector = ({ row, }) => {
|
|
2971
3178
|
const { table } = useDataTableContext();
|
|
2972
3179
|
const SELECTION_BOX_WIDTH = 20;
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
}
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
if (hoveredRow == current_index) {
|
|
2981
|
-
return true;
|
|
2982
|
-
}
|
|
2983
|
-
return false;
|
|
2984
|
-
};
|
|
2985
|
-
return (jsxRuntime.jsxs(react.Table.Cell, { padding: `${table.getDensityValue()}px`, ...(table.getIsSomeColumnsPinned("left")
|
|
2986
|
-
? {
|
|
2987
|
-
left: `0px`,
|
|
2988
|
-
backgroundColor: pinnedBgColor.light,
|
|
2989
|
-
position: "sticky",
|
|
2990
|
-
zIndex: 1,
|
|
2991
|
-
_dark: { backgroundColor: pinnedBgColor.dark },
|
|
2992
|
-
}
|
|
2993
|
-
: {}),
|
|
2994
|
-
// styling resize and pinning end
|
|
2995
|
-
display: "grid", children: [!isCheckBoxVisible(index, row) && (jsxRuntime.jsx(react.Box, { as: "span", margin: "0rem", display: "grid", justifyItems: "center", alignItems: "center", width: `${SELECTION_BOX_WIDTH}px`, height: `${SELECTION_BOX_WIDTH}px` })), isCheckBoxVisible(index, row) && (jsxRuntime.jsx(react.Box, { margin: "0rem", display: "grid", justifyItems: "center", alignItems: "center", children: jsxRuntime.jsx(Checkbox, { width: `${SELECTION_BOX_WIDTH}px`, height: `${SELECTION_BOX_WIDTH}px`, isChecked: row.getIsSelected(),
|
|
2996
|
-
disabled: !row.getCanSelect(),
|
|
2997
|
-
onChange: row.getToggleSelectedHandler() }) }))] }));
|
|
3180
|
+
return (jsxRuntime.jsx(react.Table.Cell, { padding: `${table.getDensityValue()}px`, display: "grid", color: {
|
|
3181
|
+
base: "colorPalette.900",
|
|
3182
|
+
_dark: "colorPalette.100",
|
|
3183
|
+
},
|
|
3184
|
+
bg: { base: "colorPalette.50", _dark: "colorPalette.950" }, justifyItems: "center", alignItems: "center", children: jsxRuntime.jsx(Checkbox, { width: `${SELECTION_BOX_WIDTH}px`, height: `${SELECTION_BOX_WIDTH}px`, checked: row.getIsSelected(),
|
|
3185
|
+
disabled: !row.getCanSelect(),
|
|
3186
|
+
onCheckedChange: row.getToggleSelectedHandler() }) }));
|
|
2998
3187
|
};
|
|
2999
3188
|
|
|
3000
|
-
const
|
|
3001
|
-
|
|
3002
|
-
if (disabled)
|
|
3003
|
-
return children;
|
|
3004
|
-
return (jsxRuntime.jsxs(react.Tooltip.Root, { ...rest, children: [jsxRuntime.jsx(react.Tooltip.Trigger, { asChild: true, children: children }), jsxRuntime.jsx(react.Portal, { disabled: !portalled, container: portalRef, children: jsxRuntime.jsx(react.Tooltip.Positioner, { children: jsxRuntime.jsxs(react.Tooltip.Content, { ref: ref, ...contentProps, children: [showArrow && (jsxRuntime.jsx(react.Tooltip.Arrow, { children: jsxRuntime.jsx(react.Tooltip.ArrowTip, {}) })), content] }) }) })] }));
|
|
3005
|
-
});
|
|
3006
|
-
|
|
3007
|
-
const InputGroup = React__namespace.forwardRef(function InputGroup(props, ref) {
|
|
3008
|
-
const { startElement, startElementProps, endElement, endElementProps, children, startOffset = "6px", endOffset = "6px", ...rest } = props;
|
|
3009
|
-
return (jsxRuntime.jsxs(react.Group, { ref: ref, ...rest, children: [startElement && (jsxRuntime.jsx(react.InputElement, { pointerEvents: "none", ...startElementProps, children: startElement })), React__namespace.cloneElement(children, {
|
|
3010
|
-
...(startElement && {
|
|
3011
|
-
ps: `calc(var(--input-height) - ${startOffset})`,
|
|
3012
|
-
}),
|
|
3013
|
-
...(endElement && { pe: `calc(var(--input-height) - ${endOffset})` }),
|
|
3014
|
-
// @ts-expect-error chakra generated files
|
|
3015
|
-
...children.props,
|
|
3016
|
-
}), endElement && (jsxRuntime.jsx(react.InputElement, { placement: "end", ...endElementProps, children: endElement }))] }));
|
|
3017
|
-
});
|
|
3018
|
-
|
|
3019
|
-
const GlobalFilter = () => {
|
|
3189
|
+
const TableBodySkeleton = ({ showSelector = false, canResize = true, }) => {
|
|
3190
|
+
'use no memo';
|
|
3020
3191
|
const { table } = useDataTableContext();
|
|
3021
|
-
const
|
|
3022
|
-
const
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3192
|
+
const SELECTION_BOX_WIDTH = 20;
|
|
3193
|
+
const [hoveredRow, setHoveredRow] = React.useState(-1);
|
|
3194
|
+
const handleRowHover = (index) => {
|
|
3195
|
+
setHoveredRow(index);
|
|
3196
|
+
};
|
|
3197
|
+
const getTdProps = (column) => {
|
|
3198
|
+
const tdProps = column.getIsPinned()
|
|
3199
|
+
? {
|
|
3200
|
+
left: showSelector
|
|
3201
|
+
? `${column.getStart('left') + SELECTION_BOX_WIDTH + table.getDensityValue() * 2}px`
|
|
3202
|
+
: `${column.getStart('left')}px`,
|
|
3203
|
+
position: 'relative',
|
|
3204
|
+
}
|
|
3205
|
+
: {};
|
|
3206
|
+
return tdProps;
|
|
3207
|
+
};
|
|
3208
|
+
const getTrProps = ({ hoveredRow, index, }) => {
|
|
3209
|
+
if (hoveredRow === -1) {
|
|
3210
|
+
return {};
|
|
3211
|
+
}
|
|
3212
|
+
if (hoveredRow === index) {
|
|
3213
|
+
return {
|
|
3214
|
+
opacity: '1',
|
|
3215
|
+
};
|
|
3216
|
+
}
|
|
3217
|
+
return {
|
|
3218
|
+
opacity: '0.8',
|
|
3026
3219
|
};
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
}
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
queryClient.invalidateQueries({ queryKey: [url] });
|
|
3044
|
-
}, children: [jsxRuntime.jsx(io5.IoReload, {}), " ", text] }));
|
|
3045
|
-
};
|
|
3046
|
-
|
|
3047
|
-
const FilterOptions = ({ column }) => {
|
|
3048
|
-
const { table } = useDataTableContext();
|
|
3049
|
-
const tableColumn = table.getColumn(column);
|
|
3050
|
-
const options = tableColumn?.columnDef.meta?.filterOptions ?? [];
|
|
3051
|
-
return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: options.map((option) => {
|
|
3052
|
-
const selected = table.getColumn(column)?.getFilterValue() === option;
|
|
3053
|
-
return (jsxRuntime.jsxs(react.Button, { size: "sm", onClick: () => {
|
|
3054
|
-
if (selected) {
|
|
3055
|
-
table.setColumnFilters((state) => {
|
|
3056
|
-
return state.filter((filter) => {
|
|
3057
|
-
return filter.id !== column;
|
|
3058
|
-
});
|
|
3059
|
-
});
|
|
3060
|
-
return;
|
|
3061
|
-
}
|
|
3062
|
-
table.getColumn(column)?.setFilterValue(option);
|
|
3063
|
-
}, variant: selected ? "solid" : "outline", display: "flex", gap: "0.25rem", children: [option, selected && jsxRuntime.jsx(md.MdClose, {})] }, option));
|
|
3220
|
+
};
|
|
3221
|
+
// Get the number of skeleton rows based on current pageSize
|
|
3222
|
+
const pageSize = table.getState().pagination.pageSize;
|
|
3223
|
+
const visibleColumns = table.getVisibleLeafColumns();
|
|
3224
|
+
return (jsxRuntime.jsx(react.Table.Body, { children: Array.from({ length: pageSize }).map((_, rowIndex) => {
|
|
3225
|
+
return (jsxRuntime.jsxs(react.Table.Row, { display: 'flex', zIndex: 1, onMouseEnter: () => handleRowHover(rowIndex), onMouseLeave: () => handleRowHover(-1), ...getTrProps({ hoveredRow, index: rowIndex }), children: [showSelector && jsxRuntime.jsx(TableRowSelectorSkeleton, {}), visibleColumns.map((column, colIndex) => {
|
|
3226
|
+
return (jsxRuntime.jsx(react.Table.Cell, { padding: `${table.getDensityValue()}px`,
|
|
3227
|
+
// styling resize and pinning start
|
|
3228
|
+
flex: `${canResize ? '0' : '1'} 0 ${column.getSize()}px`,
|
|
3229
|
+
// this is to avoid the cell from being too wide
|
|
3230
|
+
minWidth: `0`, color: {
|
|
3231
|
+
base: 'colorPalette.900',
|
|
3232
|
+
_dark: 'colorPalette.100',
|
|
3233
|
+
},
|
|
3234
|
+
bg: { base: 'colorPalette.50', _dark: 'colorPalette.950' }, ...getTdProps(column), children: jsxRuntime.jsx(react.Skeleton, { height: "20px", width: "80%" }) }, `chakra-table-skeleton-cell-${rowIndex}-${colIndex}`));
|
|
3235
|
+
})] }, `chakra-table-skeleton-row-${rowIndex}`));
|
|
3064
3236
|
}) }));
|
|
3065
3237
|
};
|
|
3066
|
-
|
|
3067
|
-
const TableFilterTags = () => {
|
|
3238
|
+
const TableRowSelectorSkeleton = () => {
|
|
3068
3239
|
const { table } = useDataTableContext();
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
}) }));
|
|
3076
|
-
};
|
|
3077
|
-
|
|
3078
|
-
const TableControls = ({ fitTableWidth = false, fitTableHeight = false, children = jsxRuntime.jsx(jsxRuntime.Fragment, {}), showGlobalFilter = false, showFilter = false, showFilterName = false, showFilterTags = false, showReload = false, showPagination = true, showPageSizeControl = true, showPageCountText = true, showView = true, filterOptions = [], extraItems = jsxRuntime.jsx(jsxRuntime.Fragment, {}), loading = false, hasError = false, }) => {
|
|
3079
|
-
const { translate } = useDataTableContext();
|
|
3080
|
-
return (jsxRuntime.jsxs(react.Grid, { templateRows: "auto 1fr auto", width: fitTableWidth ? "fit-content" : "100%", height: fitTableHeight ? "fit-content" : "100%", gap: "0.5rem", children: [jsxRuntime.jsxs(react.Flex, { flexFlow: "column", gap: 2, children: [jsxRuntime.jsxs(react.Flex, { justifyContent: "space-between", children: [jsxRuntime.jsx(react.Box, { children: showView && jsxRuntime.jsx(ViewDialog, { icon: jsxRuntime.jsx(md.MdOutlineViewColumn, {}) }) }), jsxRuntime.jsxs(react.Flex, { gap: "0.5rem", alignItems: "center", justifySelf: "end", children: [loading && jsxRuntime.jsx(react.Spinner, { size: "sm" }), hasError && (jsxRuntime.jsx(Tooltip, { content: translate.t("hasError"), children: jsxRuntime.jsx(react.Icon, { as: bs.BsExclamationCircleFill, color: "red.400" }) })), showGlobalFilter && jsxRuntime.jsx(GlobalFilter, {}), showFilter && jsxRuntime.jsx(FilterDialog, {}), showReload && jsxRuntime.jsx(ReloadButton, {}), extraItems] })] }), filterOptions.length > 0 && (jsxRuntime.jsx(react.Flex, { flexFlow: "column", gap: "0.5rem", children: filterOptions.map((column) => {
|
|
3081
|
-
return (jsxRuntime.jsxs(react.Flex, { alignItems: "center", flexFlow: "wrap", gap: "0.5rem", children: [showFilterName && jsxRuntime.jsxs(react.Text, { children: [column, ":"] }), jsxRuntime.jsx(FilterOptions, { column: column })] }, column));
|
|
3082
|
-
}) })), showFilterTags && (jsxRuntime.jsx(react.Flex, { children: jsxRuntime.jsx(TableFilterTags, {}) }))] }), jsxRuntime.jsx(react.Grid, { overflow: "auto", backgroundColor: "gray.50", _dark: {
|
|
3083
|
-
backgroundColor: "gray.900",
|
|
3084
|
-
}, children: children }), jsxRuntime.jsxs(react.Flex, { justifyContent: "space-between", children: [jsxRuntime.jsxs(react.Flex, { gap: "1rem", alignItems: "center", children: [showPageSizeControl && jsxRuntime.jsx(PageSizeControl, {}), showPageCountText && (jsxRuntime.jsxs(react.Flex, { children: [jsxRuntime.jsx(react.Text, { paddingRight: "0.5rem", children: translate.t("rowcount.total") }), jsxRuntime.jsx(RowCountText, {})] }))] }), jsxRuntime.jsx(react.Box, { justifySelf: "end", children: showPagination && jsxRuntime.jsx(Pagination, {}) })] })] }));
|
|
3240
|
+
const SELECTION_BOX_WIDTH = 20;
|
|
3241
|
+
return (jsxRuntime.jsx(react.Table.Cell, { padding: `${table.getDensityValue()}px`, display: 'grid', color: {
|
|
3242
|
+
base: 'colorPalette.900',
|
|
3243
|
+
_dark: 'colorPalette.100',
|
|
3244
|
+
},
|
|
3245
|
+
bg: { base: 'colorPalette.50', _dark: 'colorPalette.950' }, justifyItems: 'center', alignItems: 'center', children: jsxRuntime.jsx(react.Skeleton, { width: `${SELECTION_BOX_WIDTH}px`, height: `${SELECTION_BOX_WIDTH}px` }) }));
|
|
3085
3246
|
};
|
|
3086
3247
|
|
|
3087
|
-
const TableFooter = ({
|
|
3248
|
+
const TableFooter = ({ showSelector = false, alwaysShowSelector = true, }) => {
|
|
3088
3249
|
const table = useDataTableContext().table;
|
|
3089
3250
|
const SELECTION_BOX_WIDTH = 20;
|
|
3090
3251
|
const [hoveredCheckBox, setHoveredCheckBox] = React.useState(false);
|
|
@@ -3103,65 +3264,62 @@ const TableFooter = ({ pinnedBgColor = { light: "gray.50", dark: "gray.700" }, s
|
|
|
3103
3264
|
}
|
|
3104
3265
|
return false;
|
|
3105
3266
|
};
|
|
3106
|
-
|
|
3107
|
-
const thProps = header.column.getIsPinned()
|
|
3108
|
-
? {
|
|
3109
|
-
left: showSelector
|
|
3110
|
-
? `${header.getStart("left") + SELECTION_BOX_WIDTH + table.getDensityValue() * 2}px`
|
|
3111
|
-
: `${header.getStart("left") + table.getDensityValue() * 2}px`,
|
|
3112
|
-
background: pinnedBgColor.light,
|
|
3113
|
-
position: "sticky",
|
|
3114
|
-
zIndex: 1,
|
|
3115
|
-
_dark: {
|
|
3116
|
-
backgroundColor: pinnedBgColor.dark,
|
|
3117
|
-
},
|
|
3118
|
-
}
|
|
3119
|
-
: {};
|
|
3120
|
-
return thProps;
|
|
3121
|
-
};
|
|
3122
|
-
return (jsxRuntime.jsx(react.Table.Footer, { children: table.getFooterGroups().map((footerGroup) => (jsxRuntime.jsxs(react.Table.Row, { display: "flex", children: [showSelector && (jsxRuntime.jsxs(react.Table.Header
|
|
3123
|
-
// styling resize and pinning start
|
|
3124
|
-
, {
|
|
3125
|
-
// styling resize and pinning start
|
|
3126
|
-
padding: `${table.getDensityValue()}px`, ...(table.getIsSomeColumnsPinned("left")
|
|
3127
|
-
? {
|
|
3128
|
-
left: `0px`,
|
|
3129
|
-
backgroundColor: pinnedBgColor.light,
|
|
3130
|
-
position: "sticky",
|
|
3131
|
-
zIndex: 1,
|
|
3132
|
-
_dark: { backgroundColor: pinnedBgColor.dark },
|
|
3133
|
-
}
|
|
3134
|
-
: {}),
|
|
3135
|
-
// styling resize and pinning end
|
|
3136
|
-
onMouseEnter: () => handleRowHover(true), onMouseLeave: () => handleRowHover(false), display: "grid", children: [isCheckBoxVisible() && (jsxRuntime.jsx(react.Box, { margin: "0rem", display: "grid", justifyItems: "center", alignItems: "center", children: jsxRuntime.jsx(Checkbox, { width: `${SELECTION_BOX_WIDTH}px`, height: `${SELECTION_BOX_WIDTH}px`, isChecked: table.getIsAllRowsSelected(),
|
|
3267
|
+
return (jsxRuntime.jsx(react.Table.Footer, { children: table.getFooterGroups().map((footerGroup) => (jsxRuntime.jsxs(react.Table.Row, { display: "flex", children: [showSelector && (jsxRuntime.jsxs(react.Table.Header, { padding: `${table.getDensityValue()}px`, onMouseEnter: () => handleRowHover(true), onMouseLeave: () => handleRowHover(false), display: "grid", children: [isCheckBoxVisible() && (jsxRuntime.jsx(react.Box, { margin: "0rem", display: "grid", justifyItems: "center", alignItems: "center", children: jsxRuntime.jsx(Checkbox, { width: `${SELECTION_BOX_WIDTH}px`, height: `${SELECTION_BOX_WIDTH}px`, isChecked: table.getIsAllRowsSelected(),
|
|
3137
3268
|
// indeterminate: table.getIsSomeRowsSelected(),
|
|
3138
3269
|
onChange: table.getToggleAllRowsSelectedHandler() }) })), !isCheckBoxVisible() && (jsxRuntime.jsx(react.Box, { as: "span", margin: "0rem", display: "grid", justifyItems: "center", alignItems: "center", width: `${SELECTION_BOX_WIDTH}px`, height: `${SELECTION_BOX_WIDTH}px` }))] })), footerGroup.headers.map((header) => (jsxRuntime.jsx(react.Table.Cell, { padding: "0", columnSpan: `${header.colSpan}`,
|
|
3139
3270
|
// styling resize and pinning start
|
|
3140
|
-
maxWidth: `${header.getSize()}px`, width: `${header.getSize()}px`, display: "grid",
|
|
3271
|
+
maxWidth: `${header.getSize()}px`, width: `${header.getSize()}px`, display: "grid", children: jsxRuntime.jsx(react.MenuRoot, { children: jsxRuntime.jsx(react.MenuTrigger, { asChild: true, children: jsxRuntime.jsx(react.Box, { padding: `${table.getDensityValue()}px`, display: "flex", alignItems: "center", justifyContent: "start", borderRadius: "0rem", children: jsxRuntime.jsxs(react.Flex, { gap: "0.5rem", alignItems: "center", children: [header.isPlaceholder
|
|
3141
3272
|
? null
|
|
3142
|
-
: reactTable.flexRender(header.column.columnDef.footer, header.getContext()), jsxRuntime.jsx(react.Box, { children: header.column.getCanSort() && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [header.column.getIsSorted() === false && (
|
|
3143
|
-
// <UpDownIcon />
|
|
3144
|
-
jsxRuntime.jsx(jsxRuntime.Fragment, {})), header.column.getIsSorted() === "asc" && (jsxRuntime.jsx(bi.BiUpArrow, {})), header.column.getIsSorted() === "desc" && (jsxRuntime.jsx(bi.BiDownArrow, {}))] })) })] }) }) }) }) }, `chakra-table-footer-${header.column.id}-${footerGroup.id}`)))] }, `chakra-table-footergroup-${footerGroup.id}`))) }));
|
|
3273
|
+
: reactTable.flexRender(header.column.columnDef.footer, header.getContext()), jsxRuntime.jsx(react.Box, { children: header.column.getCanSort() && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [header.column.getIsSorted() === false && jsxRuntime.jsx(jsxRuntime.Fragment, {}), header.column.getIsSorted() === "asc" && (jsxRuntime.jsx(bi.BiUpArrow, {})), header.column.getIsSorted() === "desc" && (jsxRuntime.jsx(bi.BiDownArrow, {}))] })) })] }) }) }) }) }, `chakra-table-footer-${header.column.id}-${footerGroup.id}`)))] }, `chakra-table-footergroup-${footerGroup.id}`))) }));
|
|
3145
3274
|
};
|
|
3146
3275
|
|
|
3147
|
-
|
|
3276
|
+
// Default text values
|
|
3277
|
+
const DEFAULT_HEADER_TEXTS = {
|
|
3278
|
+
pinColumn: "Pin Column",
|
|
3279
|
+
cancelPin: "Cancel Pin",
|
|
3280
|
+
sortAscending: "Sort Ascending",
|
|
3281
|
+
sortDescending: "Sort Descending",
|
|
3282
|
+
clearSorting: "Clear Sorting",
|
|
3283
|
+
};
|
|
3284
|
+
/**
|
|
3285
|
+
* TableHeader component with configurable text strings.
|
|
3286
|
+
*
|
|
3287
|
+
* @example
|
|
3288
|
+
* // Using default texts
|
|
3289
|
+
* <TableHeader />
|
|
3290
|
+
*
|
|
3291
|
+
* @example
|
|
3292
|
+
* // Customizing default texts for all columns
|
|
3293
|
+
* <TableHeader
|
|
3294
|
+
* defaultTexts={{
|
|
3295
|
+
* pinColumn: "Pin This Column",
|
|
3296
|
+
* sortAscending: "Sort A-Z"
|
|
3297
|
+
* }}
|
|
3298
|
+
* />
|
|
3299
|
+
*
|
|
3300
|
+
* @example
|
|
3301
|
+
* // Customizing texts per column via meta
|
|
3302
|
+
* const columns = [
|
|
3303
|
+
* columnHelper.accessor("name", {
|
|
3304
|
+
* header: "Name",
|
|
3305
|
+
* meta: {
|
|
3306
|
+
* headerTexts: {
|
|
3307
|
+
* pinColumn: "Pin Name Column",
|
|
3308
|
+
* sortAscending: "Sort Names A-Z"
|
|
3309
|
+
* }
|
|
3310
|
+
* }
|
|
3311
|
+
* })
|
|
3312
|
+
* ];
|
|
3313
|
+
*/
|
|
3314
|
+
const TableHeader = ({ canResize = true, showSelector = false, isSticky = true, tableHeaderProps = {}, tableRowProps = {}, defaultTexts = {}, }) => {
|
|
3148
3315
|
const { table } = useDataTableContext();
|
|
3149
3316
|
const SELECTION_BOX_WIDTH = 20;
|
|
3150
|
-
|
|
3151
|
-
const
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
return true;
|
|
3157
|
-
}
|
|
3158
|
-
if (table.getIsAllRowsSelected()) {
|
|
3159
|
-
return true;
|
|
3160
|
-
}
|
|
3161
|
-
if (hoveredCheckBox) {
|
|
3162
|
-
return true;
|
|
3163
|
-
}
|
|
3164
|
-
return false;
|
|
3317
|
+
// Merge default texts with provided defaults
|
|
3318
|
+
const mergedDefaultTexts = { ...DEFAULT_HEADER_TEXTS, ...defaultTexts };
|
|
3319
|
+
// Helper function to get text for a specific header
|
|
3320
|
+
const getHeaderText = (header, key) => {
|
|
3321
|
+
const columnMeta = header.column.columnDef.meta;
|
|
3322
|
+
return columnMeta?.headerTexts?.[key] || mergedDefaultTexts[key];
|
|
3165
3323
|
};
|
|
3166
3324
|
const getThProps = (header) => {
|
|
3167
3325
|
const thProps = header.column.getIsPinned()
|
|
@@ -3169,12 +3327,8 @@ const TableHeader = ({ canResize = true, pinnedBgColor = { light: "gray.50", dar
|
|
|
3169
3327
|
left: showSelector
|
|
3170
3328
|
? `${header.getStart("left") + SELECTION_BOX_WIDTH + table.getDensityValue() * 2}px`
|
|
3171
3329
|
: `${header.getStart("left")}px`,
|
|
3172
|
-
background: pinnedBgColor.light,
|
|
3173
3330
|
position: "sticky",
|
|
3174
3331
|
zIndex: 100 + 1,
|
|
3175
|
-
_dark: {
|
|
3176
|
-
backgroundColor: pinnedBgColor.dark,
|
|
3177
|
-
},
|
|
3178
3332
|
}
|
|
3179
3333
|
: {};
|
|
3180
3334
|
return thProps;
|
|
@@ -3183,21 +3337,13 @@ const TableHeader = ({ canResize = true, pinnedBgColor = { light: "gray.50", dar
|
|
|
3183
3337
|
position: "sticky",
|
|
3184
3338
|
top: 0,
|
|
3185
3339
|
};
|
|
3186
|
-
return (jsxRuntime.jsx(react.Table.Header, { ...(isSticky ? stickyProps : {}), ...
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
zIndex: 1,
|
|
3194
|
-
_dark: { backgroundColor: pinnedBgColor.dark },
|
|
3195
|
-
}
|
|
3196
|
-
: {}),
|
|
3197
|
-
// styling resize and pinning end
|
|
3198
|
-
padding: `${table.getDensityValue()}px`, onMouseEnter: () => handleRowHover(true), onMouseLeave: () => handleRowHover(false), display: "grid", children: [isCheckBoxVisible() && (jsxRuntime.jsx(react.Box, { margin: "0rem", display: "grid", justifyItems: "center", alignItems: "center", children: jsxRuntime.jsx(Checkbox, { width: `${SELECTION_BOX_WIDTH}px`, height: `${SELECTION_BOX_WIDTH}px`, isChecked: table.getIsAllRowsSelected(),
|
|
3199
|
-
// indeterminate: table.getIsSomeRowsSelected(),
|
|
3200
|
-
onChange: table.getToggleAllRowsSelectedHandler() }) })), !isCheckBoxVisible() && (jsxRuntime.jsx(react.Box, { as: "span", margin: "0rem", display: "grid", justifyItems: "center", alignItems: "center", width: `${SELECTION_BOX_WIDTH}px`, height: `${SELECTION_BOX_WIDTH}px` }))] })), headerGroup.headers.map((header) => {
|
|
3340
|
+
return (jsxRuntime.jsx(react.Table.Header, { ...(isSticky ? stickyProps : {}), bgColor: "transparent", ...tableHeaderProps, children: table.getHeaderGroups().map((headerGroup) => (jsxRuntime.jsxs(react.Table.Row, { display: "flex", bgColor: "transparent", ...tableRowProps, children: [showSelector && (jsxRuntime.jsx(react.Table.ColumnHeader, { padding: `${table.getDensityValue()}px`, display: "grid", color: {
|
|
3341
|
+
base: "colorPalette.900",
|
|
3342
|
+
_dark: "colorPalette.100",
|
|
3343
|
+
},
|
|
3344
|
+
bg: { base: "colorPalette.50", _dark: "colorPalette.950" }, justifyItems: "center", alignItems: "center", children: jsxRuntime.jsx(Checkbox, { width: `${SELECTION_BOX_WIDTH}px`, height: `${SELECTION_BOX_WIDTH}px`, checked: table.getIsAllRowsSelected(),
|
|
3345
|
+
// indeterminate: table.getIsSomeRowsSelected(),
|
|
3346
|
+
onChange: table.getToggleAllRowsSelectedHandler() }) })), headerGroup.headers.map((header) => {
|
|
3201
3347
|
const resizeProps = {
|
|
3202
3348
|
onMouseDown: header.getResizeHandler(),
|
|
3203
3349
|
onTouchStart: header.getResizeHandler(),
|
|
@@ -3205,18 +3351,32 @@ const TableHeader = ({ canResize = true, pinnedBgColor = { light: "gray.50", dar
|
|
|
3205
3351
|
};
|
|
3206
3352
|
return (jsxRuntime.jsxs(react.Table.ColumnHeader, { padding: 0, columnSpan: `${header.colSpan}`,
|
|
3207
3353
|
// styling resize and pinning start
|
|
3208
|
-
flex: `${canResize ? "0" : "1"} 0 ${header.column.getSize()}px`, display: "grid", gridTemplateColumns: "1fr auto", zIndex: 1500 + header.index,
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3354
|
+
flex: `${canResize ? "0" : "1"} 0 ${header.column.getSize()}px`, display: "grid", gridTemplateColumns: "1fr auto", zIndex: 1500 + header.index, color: {
|
|
3355
|
+
base: "colorPalette.800",
|
|
3356
|
+
_dark: "colorPalette.200",
|
|
3357
|
+
},
|
|
3358
|
+
bg: { base: "colorPalette.100", _dark: "colorPalette.900" }, ...getThProps(header), children: [jsxRuntime.jsxs(MenuRoot, { children: [jsxRuntime.jsx(MenuTrigger, { asChild: true, children: jsxRuntime.jsx(react.Flex, { padding: `${table.getDensityValue()}px`, alignItems: "center", justifyContent: "start", borderRadius: "0rem", overflow: "auto", color: {
|
|
3359
|
+
base: "colorPalette.800",
|
|
3360
|
+
_dark: "colorPalette.200",
|
|
3361
|
+
_hover: {
|
|
3362
|
+
base: "colorPalette.700",
|
|
3363
|
+
_dark: "colorPalette.300",
|
|
3364
|
+
},
|
|
3365
|
+
},
|
|
3366
|
+
bg: {
|
|
3367
|
+
base: "colorPalette.100",
|
|
3368
|
+
_dark: "colorPalette.900",
|
|
3369
|
+
_hover: {
|
|
3370
|
+
base: "colorPalette.200",
|
|
3371
|
+
_dark: "colorPalette.800",
|
|
3212
3372
|
},
|
|
3213
3373
|
}, children: jsxRuntime.jsxs(react.Flex, { gap: "0.5rem", alignItems: "center", children: [header.isPlaceholder
|
|
3214
3374
|
? null
|
|
3215
3375
|
: reactTable.flexRender(header.column.columnDef.header, header.getContext()), jsxRuntime.jsx(react.Box, { children: header.column.getCanSort() && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [header.column.getIsSorted() === false && jsxRuntime.jsx(jsxRuntime.Fragment, {}), header.column.getIsSorted() === "asc" && (jsxRuntime.jsx(bi.BiUpArrow, {})), header.column.getIsSorted() === "desc" && (jsxRuntime.jsx(bi.BiDownArrow, {}))] })) }), jsxRuntime.jsx(react.Box, { children: header.column.getIsFiltered() && jsxRuntime.jsx(md.MdFilterListAlt, {}) })] }) }) }), jsxRuntime.jsxs(MenuContent, { children: [!header.column.getIsPinned() && (jsxRuntime.jsx(MenuItem, { asChild: true, value: "pin-column", children: jsxRuntime.jsxs(Button, { variant: "ghost", onClick: () => {
|
|
3216
3376
|
header.column.pin("left");
|
|
3217
|
-
}, children: [jsxRuntime.jsx(md.MdPushPin, {}),
|
|
3377
|
+
}, children: [jsxRuntime.jsx(md.MdPushPin, {}), getHeaderText(header, "pinColumn")] }) })), header.column.getIsPinned() && (jsxRuntime.jsx(MenuItem, { asChild: true, value: "cancel-pin", children: jsxRuntime.jsxs(Button, { variant: "ghost", onClick: () => {
|
|
3218
3378
|
header.column.pin(false);
|
|
3219
|
-
}, children: [jsxRuntime.jsx(md.MdCancel, {}),
|
|
3379
|
+
}, children: [jsxRuntime.jsx(md.MdCancel, {}), getHeaderText(header, "cancelPin")] }) })), header.column.getCanSort() && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(MenuItem, { asChild: true, value: "sort-ascend", children: jsxRuntime.jsxs(Button, { variant: "ghost", onClick: () => {
|
|
3220
3380
|
table.setSorting((state) => {
|
|
3221
3381
|
return [
|
|
3222
3382
|
...state.filter((column) => {
|
|
@@ -3225,7 +3385,7 @@ const TableHeader = ({ canResize = true, pinnedBgColor = { light: "gray.50", dar
|
|
|
3225
3385
|
{ id: header.id, desc: false },
|
|
3226
3386
|
];
|
|
3227
3387
|
});
|
|
3228
|
-
}, children: [jsxRuntime.jsx(gr.GrAscend, {}),
|
|
3388
|
+
}, children: [jsxRuntime.jsx(gr.GrAscend, {}), getHeaderText(header, "sortAscending")] }) }), jsxRuntime.jsx(MenuItem, { asChild: true, value: "sort-descend", children: jsxRuntime.jsxs(Button, { variant: "ghost", onClick: () => {
|
|
3229
3389
|
table.setSorting((state) => {
|
|
3230
3390
|
return [
|
|
3231
3391
|
...state.filter((column) => {
|
|
@@ -3234,42 +3394,53 @@ const TableHeader = ({ canResize = true, pinnedBgColor = { light: "gray.50", dar
|
|
|
3234
3394
|
{ id: header.id, desc: true },
|
|
3235
3395
|
];
|
|
3236
3396
|
});
|
|
3237
|
-
}, children: [jsxRuntime.jsx(gr.GrDescend, {}),
|
|
3397
|
+
}, children: [jsxRuntime.jsx(gr.GrDescend, {}), getHeaderText(header, "sortDescending")] }) }), header.column.getIsSorted() && (jsxRuntime.jsx(MenuItem, { asChild: true, value: "clear-sorting", children: jsxRuntime.jsxs(Button, { variant: "ghost", onClick: () => {
|
|
3238
3398
|
header.column.clearSorting();
|
|
3239
|
-
}, children: [jsxRuntime.jsx(md.MdClear, {}),
|
|
3399
|
+
}, children: [jsxRuntime.jsx(md.MdClear, {}), getHeaderText(header, "clearSorting")] }) }))] }))] })] }), canResize && (jsxRuntime.jsx(react.Box, { borderRight: "0.2rem solid", borderRightColor: header.column.getIsResizing()
|
|
3400
|
+
? "colorPalette.700"
|
|
3401
|
+
: "transparent", position: "relative", right: "0.1rem", width: "2px", height: "100%", userSelect: "none", style: { touchAction: "none" }, _hover: {
|
|
3240
3402
|
borderRightColor: header.column.getIsResizing()
|
|
3241
|
-
? "
|
|
3242
|
-
: "
|
|
3403
|
+
? "colorPalette.700"
|
|
3404
|
+
: "colorPalette.400",
|
|
3243
3405
|
}, ...resizeProps }))] }, `chakra-table-header-${header.id}`));
|
|
3244
3406
|
})] }, `chakra-table-headergroup-${headerGroup.id}`))) }));
|
|
3245
3407
|
};
|
|
3246
3408
|
|
|
3247
|
-
const
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
});
|
|
3251
|
-
|
|
3252
|
-
const EmptyResult = (jsxRuntime.jsx(EmptyState, { icon: jsxRuntime.jsx(hi.HiColorSwatch, {}), title: "No results found", description: "Try adjusting your search", children: jsxRuntime.jsxs(react.List.Root, { variant: "marker", children: [jsxRuntime.jsx(react.List.Item, { children: "Try removing filters" }), jsxRuntime.jsx(react.List.Item, { children: "Try different keywords" })] }) }));
|
|
3253
|
-
const Table = ({ children, emptyComponent = EmptyResult, canResize = true, ...props }) => {
|
|
3254
|
-
const { table } = useDataTableContext();
|
|
3255
|
-
if (table.getRowModel().rows.length <= 0) {
|
|
3256
|
-
return emptyComponent;
|
|
3409
|
+
const DefaultTable = ({ showFooter = false, tableProps = {}, tableHeaderProps = {}, tableBodyProps = {}, tableFooterProps = {}, controlProps = {}, variant = '', isLoading = false, }) => {
|
|
3410
|
+
if (variant === 'greedy') {
|
|
3411
|
+
const bodyComponent = isLoading ? (jsxRuntime.jsx(TableBodySkeleton, { showSelector: tableBodyProps.showSelector, canResize: false })) : (jsxRuntime.jsx(TableBody, { ...tableBodyProps, canResize: false, ...tableBodyProps }));
|
|
3412
|
+
return (jsxRuntime.jsx(TableControls, { ...controlProps, children: jsxRuntime.jsxs(Table, { canResize: false, showLoading: isLoading, ...tableProps, children: [jsxRuntime.jsx(TableHeader, { canResize: false, ...tableHeaderProps }), bodyComponent, showFooter && (jsxRuntime.jsx(TableFooter, { canResize: false, ...tableFooterProps }))] }) }));
|
|
3257
3413
|
}
|
|
3258
|
-
|
|
3414
|
+
const bodyComponent = isLoading ? (jsxRuntime.jsx(TableBodySkeleton, { showSelector: tableBodyProps.showSelector, canResize: tableBodyProps.canResize })) : (jsxRuntime.jsx(TableBody, { ...tableBodyProps }));
|
|
3415
|
+
return (jsxRuntime.jsx(TableControls, { ...controlProps, children: jsxRuntime.jsxs(Table, { showLoading: isLoading, ...tableProps, children: [jsxRuntime.jsx(TableHeader, { ...tableHeaderProps }), bodyComponent, showFooter && jsxRuntime.jsx(TableFooter, { ...tableFooterProps })] }) }));
|
|
3259
3416
|
};
|
|
3260
3417
|
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3418
|
+
/**
|
|
3419
|
+
* DefaultTableServer is a wrapper around DefaultTable that automatically
|
|
3420
|
+
* detects server-side loading state from DataTableServerContext.
|
|
3421
|
+
*
|
|
3422
|
+
* Use this component when working with DataTableServer to automatically
|
|
3423
|
+
* show skeleton loading state during data fetching.
|
|
3424
|
+
*
|
|
3425
|
+
* @example
|
|
3426
|
+
* ```tsx
|
|
3427
|
+
* <DataTableServer columns={columns} {...datatableServer}>
|
|
3428
|
+
* <DefaultTableServer />
|
|
3429
|
+
* </DataTableServer>
|
|
3430
|
+
* ```
|
|
3431
|
+
*/
|
|
3432
|
+
const DefaultTableServer = ({ isLoading: isLoadingOverride, ...props }) => {
|
|
3433
|
+
// Automatically detect loading state from server context
|
|
3434
|
+
const serverContext = useDataTableServerContext();
|
|
3435
|
+
const isLoading = isLoadingOverride ?? serverContext?.query?.isLoading ?? false;
|
|
3436
|
+
return jsxRuntime.jsx(DefaultTable, { ...props, isLoading: isLoading });
|
|
3266
3437
|
};
|
|
3267
3438
|
|
|
3268
|
-
const TableCardContainer = ({ children, variant = "", ...props }) => {
|
|
3439
|
+
const TableCardContainer = ({ children, variant = "", gap = "1rem", gridTemplateColumns = "repeat(auto-fit, minmax(20rem, 1fr))", direction = "row", ...props }) => {
|
|
3269
3440
|
if (variant === "carousel") {
|
|
3270
|
-
return (jsxRuntime.jsx(react.Flex, { overflow: "
|
|
3441
|
+
return (jsxRuntime.jsx(react.Flex, { overflow: "auto", gap: gap, direction: direction, ...props, children: children }));
|
|
3271
3442
|
}
|
|
3272
|
-
return (jsxRuntime.jsx(react.Grid, { gridTemplateColumns:
|
|
3443
|
+
return (jsxRuntime.jsx(react.Grid, { gridTemplateColumns: gridTemplateColumns, gap: gap, ...props, children: children }));
|
|
3273
3444
|
};
|
|
3274
3445
|
|
|
3275
3446
|
const DefaultCardTitle = () => {
|
|
@@ -3298,8 +3469,8 @@ const TableComponent = ({ render = () => {
|
|
|
3298
3469
|
};
|
|
3299
3470
|
|
|
3300
3471
|
const TableLoadingComponent = ({ render, }) => {
|
|
3301
|
-
const {
|
|
3302
|
-
return jsxRuntime.jsx(jsxRuntime.Fragment, { children: render(
|
|
3472
|
+
const { query } = useDataTableServerContext();
|
|
3473
|
+
return jsxRuntime.jsx(jsxRuntime.Fragment, { children: render(query.isLoading) });
|
|
3303
3474
|
};
|
|
3304
3475
|
|
|
3305
3476
|
const SelectAllRowsToggle = ({ selectAllIcon = jsxRuntime.jsx(md.MdOutlineChecklist, {}), clearAllIcon = jsxRuntime.jsx(md.MdClear, {}), selectAllText = "", clearAllText = "", }) => {
|
|
@@ -3385,50 +3556,43 @@ const useDataTable = ({ default: { sorting: defaultSorting = [], pagination: def
|
|
|
3385
3556
|
};
|
|
3386
3557
|
};
|
|
3387
3558
|
|
|
3388
|
-
const useDataTableServer = (
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
pagination
|
|
3559
|
+
const useDataTableServer = (props) => {
|
|
3560
|
+
const { url, default: defaultProps, keyPrefix, placeholderData, queryFn: customQueryFn, } = props;
|
|
3561
|
+
const { sorting: defaultSorting, pagination: defaultPagination, rowSelection: defaultRowSelection, columnFilters: defaultColumnFilters, columnOrder: defaultColumnOrder, columnVisibility: defaultColumnVisibility, globalFilter: defaultGlobalFilter, density: defaultDensity, } = defaultProps || {};
|
|
3562
|
+
const [sorting, setSorting] = React.useState(defaultSorting || []);
|
|
3563
|
+
const [columnFilters, setColumnFilters] = React.useState(defaultColumnFilters || []); // can set initial column filter state here
|
|
3564
|
+
const [pagination, setPagination] = React.useState(defaultPagination || {
|
|
3394
3565
|
pageIndex: 0, //initial page index
|
|
3395
|
-
pageSize: 10, //
|
|
3396
|
-
}
|
|
3397
|
-
rowSelection
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
}, keyPrefix, }) => {
|
|
3404
|
-
const [sorting, setSorting] = React.useState(defaultSorting);
|
|
3405
|
-
const [columnFilters, setColumnFilters] = React.useState(defaultColumnFilters); // can set initial column filter state here
|
|
3406
|
-
const [pagination, setPagination] = React.useState(defaultPagination);
|
|
3407
|
-
const [rowSelection, setRowSelection] = React.useState(defaultRowSelection);
|
|
3408
|
-
const [columnOrder, setColumnOrder] = React.useState(defaultColumnOrder);
|
|
3409
|
-
const [globalFilter, setGlobalFilter] = React.useState(defaultGlobalFilter);
|
|
3410
|
-
const [density, setDensity] = React.useState(defaultDensity);
|
|
3411
|
-
const [columnVisibility, setColumnVisibility] = React.useState(defaultColumnVisibility);
|
|
3566
|
+
pageSize: 10, //default page size
|
|
3567
|
+
});
|
|
3568
|
+
const [rowSelection, setRowSelection] = React.useState(defaultRowSelection || {});
|
|
3569
|
+
const [columnOrder, setColumnOrder] = React.useState(defaultColumnOrder || []);
|
|
3570
|
+
const [globalFilter, setGlobalFilter] = React.useState(defaultGlobalFilter || "");
|
|
3571
|
+
const [density, setDensity] = React.useState(defaultDensity || "sm");
|
|
3572
|
+
const [columnVisibility, setColumnVisibility] = React.useState(defaultColumnVisibility || {});
|
|
3573
|
+
const { pageSize, pageIndex } = pagination;
|
|
3412
3574
|
const params = {
|
|
3413
|
-
offset:
|
|
3414
|
-
limit:
|
|
3575
|
+
offset: pageIndex * pageSize,
|
|
3576
|
+
limit: pageSize,
|
|
3415
3577
|
sorting,
|
|
3416
3578
|
where: columnFilters,
|
|
3417
3579
|
searching: globalFilter,
|
|
3418
3580
|
};
|
|
3581
|
+
const defaultQueryFn = async () => {
|
|
3582
|
+
if (!url) {
|
|
3583
|
+
throw new Error("url is required");
|
|
3584
|
+
}
|
|
3585
|
+
const response = await axios.get(url, {
|
|
3586
|
+
params,
|
|
3587
|
+
});
|
|
3588
|
+
return response.data;
|
|
3589
|
+
};
|
|
3419
3590
|
const query = reactQuery.useQuery({
|
|
3420
3591
|
queryKey: [url, JSON.stringify(params)],
|
|
3421
|
-
queryFn:
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
})
|
|
3426
|
-
.then((res) => res.data);
|
|
3427
|
-
},
|
|
3428
|
-
placeholderData: {
|
|
3429
|
-
count: 0,
|
|
3430
|
-
data: [],
|
|
3431
|
-
},
|
|
3592
|
+
queryFn: customQueryFn !== undefined
|
|
3593
|
+
? () => customQueryFn(params)
|
|
3594
|
+
: defaultQueryFn,
|
|
3595
|
+
placeholderData,
|
|
3432
3596
|
});
|
|
3433
3597
|
const translate = reactI18next.useTranslation("", { keyPrefix });
|
|
3434
3598
|
return {
|
|
@@ -3518,23 +3682,33 @@ const getColumns = ({ schema, include = [], ignore = [], width = [], meta = {},
|
|
|
3518
3682
|
return columns;
|
|
3519
3683
|
};
|
|
3520
3684
|
|
|
3521
|
-
const TableDataDisplay = () => {
|
|
3522
|
-
const {
|
|
3523
|
-
const { query } = useDataTableServerContext();
|
|
3524
|
-
const { data } = query;
|
|
3525
|
-
const columnDef = table._getColumnDefs();
|
|
3526
|
-
console.log(columnDef, "glp");
|
|
3527
|
-
console.log(columnDef, columns, table.getState().columnOrder, data, "glp");
|
|
3685
|
+
const TableDataDisplay = ({ colorPalette, emptyComponent, }) => {
|
|
3686
|
+
const { columns, translate, data } = useDataTableContext();
|
|
3528
3687
|
const columnsMap = Object.fromEntries(columns.map((def) => {
|
|
3529
|
-
|
|
3688
|
+
const { accessorKey, id } = def;
|
|
3689
|
+
if (accessorKey) {
|
|
3690
|
+
return [accessorKey, def];
|
|
3691
|
+
}
|
|
3692
|
+
return [id, def];
|
|
3530
3693
|
}));
|
|
3531
3694
|
const columnHeaders = Object.keys(columnsMap);
|
|
3695
|
+
const totalWidths = columns
|
|
3696
|
+
.map(({ size }) => {
|
|
3697
|
+
if (!!size === false) {
|
|
3698
|
+
return 0;
|
|
3699
|
+
}
|
|
3700
|
+
if (typeof size === "number") {
|
|
3701
|
+
return size;
|
|
3702
|
+
}
|
|
3703
|
+
return 0;
|
|
3704
|
+
})
|
|
3705
|
+
.reduce((previous, current) => previous + current, 0);
|
|
3532
3706
|
const columnWidths = columns
|
|
3533
3707
|
.map(({ size }) => {
|
|
3534
3708
|
if (!!size === false) {
|
|
3535
3709
|
return "1fr";
|
|
3536
3710
|
}
|
|
3537
|
-
return
|
|
3711
|
+
return `minmax(${size}px, ${(size / totalWidths) * 100}%)`;
|
|
3538
3712
|
})
|
|
3539
3713
|
.join(" ");
|
|
3540
3714
|
console.log({ columnWidths }, "hadfg");
|
|
@@ -3543,48 +3717,55 @@ const TableDataDisplay = () => {
|
|
|
3543
3717
|
overflow: "auto",
|
|
3544
3718
|
paddingX: "2",
|
|
3545
3719
|
py: "1",
|
|
3720
|
+
color: { base: "colorPalette.900", _dark: "colorPalette.100" },
|
|
3721
|
+
bgColor: { base: "colorPalette.50", _dark: "colorPalette.950" },
|
|
3546
3722
|
borderBottomColor: { base: "colorPalette.200", _dark: "colorPalette.800" },
|
|
3547
3723
|
borderBottomWidth: "1px",
|
|
3724
|
+
...{ colorPalette },
|
|
3548
3725
|
};
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3726
|
+
if (data.length <= 0) {
|
|
3727
|
+
return jsxRuntime.jsx(jsxRuntime.Fragment, { children: emptyComponent });
|
|
3728
|
+
}
|
|
3729
|
+
return (jsxRuntime.jsxs(react.Grid, { templateColumns: `${columnWidths}`, overflow: "auto", borderWidth: "1px", color: { base: "colorPalette.900", _dark: "colorPalette.100" }, borderColor: { base: "colorPalette.200", _dark: "colorPalette.800" }, colorPalette, children: [jsxRuntime.jsx(react.Grid, { templateColumns: `${columnWidths}`, column: `1/span ${columns.length}`, bg: { base: "colorPalette.200", _dark: "colorPalette.800" }, colorPalette, children: columnHeaders.map((header) => {
|
|
3730
|
+
return (jsxRuntime.jsx(react.Box, { flex: "1 0 0%", paddingX: "2", py: "1", overflow: "auto", textOverflow: "ellipsis", children: translate.t(`column_header.${header}`) }));
|
|
3731
|
+
}) }), data.map((record) => {
|
|
3552
3732
|
return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: columnHeaders.map((header) => {
|
|
3733
|
+
const { cell } = columnsMap[header];
|
|
3734
|
+
const value = record[header];
|
|
3553
3735
|
if (!!record === false) {
|
|
3554
3736
|
return (jsxRuntime.jsx(react.Box, { ...cellProps, children: translate.t(`column_cell.placeholder`) }));
|
|
3555
3737
|
}
|
|
3556
|
-
if (
|
|
3557
|
-
return (jsxRuntime.jsx(react.Box, { ...cellProps, children:
|
|
3738
|
+
if (cell) {
|
|
3739
|
+
return (jsxRuntime.jsx(react.Box, { ...cellProps, children: cell({ row: { original: record } }) }));
|
|
3558
3740
|
}
|
|
3559
|
-
if (typeof
|
|
3560
|
-
return (jsxRuntime.jsx(react.Box, { ...cellProps, children: jsxRuntime.jsx(RecordDisplay, { object:
|
|
3741
|
+
if (typeof value === "object") {
|
|
3742
|
+
return (jsxRuntime.jsx(react.Box, { ...cellProps, children: jsxRuntime.jsx(RecordDisplay, { object: value }) }));
|
|
3561
3743
|
}
|
|
3562
|
-
return jsxRuntime.jsx(react.Box, { ...cellProps, children:
|
|
3744
|
+
return jsxRuntime.jsx(react.Box, { ...cellProps, children: value });
|
|
3563
3745
|
}) }));
|
|
3564
3746
|
})] }));
|
|
3565
3747
|
};
|
|
3566
3748
|
|
|
3567
|
-
const AccordionItemTrigger = React__namespace.forwardRef(function AccordionItemTrigger(props, ref) {
|
|
3568
|
-
const { children, indicatorPlacement = "end", ...rest } = props;
|
|
3569
|
-
return (jsxRuntime.jsxs(react.Accordion.ItemTrigger, { ...rest, ref: ref, children: [indicatorPlacement === "start" && (jsxRuntime.jsx(react.Accordion.ItemIndicator, { rotate: { base: "-90deg", _open: "0deg" }, children: jsxRuntime.jsx(lu.LuChevronDown, {}) })), jsxRuntime.jsx(react.HStack, { gap: "4", flex: "1", textAlign: "start", width: "full", children: children }), indicatorPlacement === "end" && (jsxRuntime.jsx(react.Accordion.ItemIndicator, { children: jsxRuntime.jsx(lu.LuChevronDown, {}) }))] }));
|
|
3570
|
-
});
|
|
3571
|
-
const AccordionItemContent = React__namespace.forwardRef(function AccordionItemContent(props, ref) {
|
|
3572
|
-
return (jsxRuntime.jsx(react.Accordion.ItemContent, { children: jsxRuntime.jsx(react.Accordion.ItemBody, { ...props, ref: ref }) }));
|
|
3573
|
-
});
|
|
3574
|
-
const AccordionRoot = react.Accordion.Root;
|
|
3575
|
-
const AccordionItem = react.Accordion.Item;
|
|
3576
|
-
|
|
3577
3749
|
//@ts-expect-error TODO: find appropriate type
|
|
3578
3750
|
const SchemaFormContext = React.createContext({
|
|
3579
3751
|
schema: {},
|
|
3580
|
-
serverUrl:
|
|
3581
|
-
requestUrl:
|
|
3752
|
+
serverUrl: '',
|
|
3753
|
+
requestUrl: '',
|
|
3582
3754
|
order: [],
|
|
3583
3755
|
ignore: [],
|
|
3584
3756
|
include: [],
|
|
3585
3757
|
onSubmit: async () => { },
|
|
3586
3758
|
rowNumber: 0,
|
|
3587
3759
|
requestOptions: {},
|
|
3760
|
+
timezone: 'Asia/Hong_Kong',
|
|
3761
|
+
displayConfig: {
|
|
3762
|
+
showSubmitButton: true,
|
|
3763
|
+
showResetButton: true,
|
|
3764
|
+
showTitle: true,
|
|
3765
|
+
},
|
|
3766
|
+
requireConfirmation: false,
|
|
3767
|
+
onFormSubmit: async () => { },
|
|
3768
|
+
ajvResolver: async () => ({ values: {}, errors: {} }),
|
|
3588
3769
|
});
|
|
3589
3770
|
|
|
3590
3771
|
const useSchemaContext = () => {
|
|
@@ -3595,6 +3776,202 @@ const clearEmptyString = (object) => {
|
|
|
3595
3776
|
return Object.fromEntries(Object.entries(object).filter(([, value]) => value !== ""));
|
|
3596
3777
|
};
|
|
3597
3778
|
|
|
3779
|
+
const validateData = (data, schema) => {
|
|
3780
|
+
const ajv = new Ajv({
|
|
3781
|
+
strict: false,
|
|
3782
|
+
allErrors: true,
|
|
3783
|
+
});
|
|
3784
|
+
addFormats(ajv);
|
|
3785
|
+
const validate = ajv.compile(schema);
|
|
3786
|
+
const validationResult = validate(data);
|
|
3787
|
+
const errors = validate.errors;
|
|
3788
|
+
console.log(errors, data);
|
|
3789
|
+
return {
|
|
3790
|
+
isValid: validationResult,
|
|
3791
|
+
validate,
|
|
3792
|
+
errors,
|
|
3793
|
+
};
|
|
3794
|
+
};
|
|
3795
|
+
|
|
3796
|
+
/**
|
|
3797
|
+
* Gets the schema node for a field by following the path from root schema
|
|
3798
|
+
*/
|
|
3799
|
+
const getSchemaNodeForField = (schema, fieldPath) => {
|
|
3800
|
+
if (!fieldPath || fieldPath === '') {
|
|
3801
|
+
return schema;
|
|
3802
|
+
}
|
|
3803
|
+
const pathParts = fieldPath.split('.');
|
|
3804
|
+
let currentSchema = schema;
|
|
3805
|
+
for (const part of pathParts) {
|
|
3806
|
+
if (currentSchema &&
|
|
3807
|
+
currentSchema.properties &&
|
|
3808
|
+
currentSchema.properties[part] &&
|
|
3809
|
+
typeof currentSchema.properties[part] === 'object' &&
|
|
3810
|
+
currentSchema.properties[part] !== null) {
|
|
3811
|
+
currentSchema = currentSchema.properties[part];
|
|
3812
|
+
}
|
|
3813
|
+
else {
|
|
3814
|
+
return undefined;
|
|
3815
|
+
}
|
|
3816
|
+
}
|
|
3817
|
+
return currentSchema;
|
|
3818
|
+
};
|
|
3819
|
+
/**
|
|
3820
|
+
* Converts AJV error objects to react-hook-form field errors format
|
|
3821
|
+
*/
|
|
3822
|
+
const convertAjvErrorsToFieldErrors = (errors, schema) => {
|
|
3823
|
+
if (!errors || errors.length === 0) {
|
|
3824
|
+
return {};
|
|
3825
|
+
}
|
|
3826
|
+
const fieldErrors = {};
|
|
3827
|
+
errors.forEach((error) => {
|
|
3828
|
+
let fieldName = '';
|
|
3829
|
+
// Special handling for required keyword: map to the specific missing property
|
|
3830
|
+
if (error.keyword === 'required') {
|
|
3831
|
+
const basePath = (error.instancePath || '')
|
|
3832
|
+
.replace(/^\//, '')
|
|
3833
|
+
.replace(/\//g, '.');
|
|
3834
|
+
const missingProperty = (error.params &&
|
|
3835
|
+
error.params.missingProperty);
|
|
3836
|
+
if (missingProperty) {
|
|
3837
|
+
fieldName = basePath
|
|
3838
|
+
? `${basePath}.${missingProperty}`
|
|
3839
|
+
: missingProperty;
|
|
3840
|
+
}
|
|
3841
|
+
else {
|
|
3842
|
+
// Fallback to schemaPath conversion if missingProperty is unavailable
|
|
3843
|
+
fieldName = (error.schemaPath || '')
|
|
3844
|
+
.replace(/^#\//, '#.')
|
|
3845
|
+
.replace(/\//g, '.');
|
|
3846
|
+
}
|
|
3847
|
+
}
|
|
3848
|
+
else {
|
|
3849
|
+
const fieldPath = error.instancePath || error.schemaPath;
|
|
3850
|
+
if (fieldPath) {
|
|
3851
|
+
fieldName = fieldPath.replace(/^\//, '').replace(/\//g, '.');
|
|
3852
|
+
}
|
|
3853
|
+
}
|
|
3854
|
+
if (fieldName) {
|
|
3855
|
+
// Get the schema node for this field to check for custom error messages
|
|
3856
|
+
const fieldSchema = getSchemaNodeForField(schema, fieldName);
|
|
3857
|
+
const customMessage = fieldSchema?.errorMessages?.[error.keyword];
|
|
3858
|
+
// Provide helpful fallback message if no custom message is provided
|
|
3859
|
+
const fallbackMessage = customMessage ||
|
|
3860
|
+
`Missing error message for ${error.keyword}. Add errorMessages.${error.keyword} to schema for field '${fieldName}'`;
|
|
3861
|
+
if (error.keyword === 'required') {
|
|
3862
|
+
// Required errors override any existing non-required errors for this field
|
|
3863
|
+
fieldErrors[fieldName] = {
|
|
3864
|
+
type: 'required',
|
|
3865
|
+
keyword: error.keyword,
|
|
3866
|
+
params: error.params,
|
|
3867
|
+
message: fallbackMessage,
|
|
3868
|
+
};
|
|
3869
|
+
}
|
|
3870
|
+
else {
|
|
3871
|
+
const existing = fieldErrors[fieldName];
|
|
3872
|
+
if (existing) {
|
|
3873
|
+
// Do not override required errors
|
|
3874
|
+
if (existing.type === 'required') {
|
|
3875
|
+
return;
|
|
3876
|
+
}
|
|
3877
|
+
// Combine messages if multiple errors for same field
|
|
3878
|
+
existing.message = existing.message
|
|
3879
|
+
? `${existing.message}; ${fallbackMessage}`
|
|
3880
|
+
: fallbackMessage;
|
|
3881
|
+
}
|
|
3882
|
+
else {
|
|
3883
|
+
fieldErrors[fieldName] = {
|
|
3884
|
+
type: error.keyword,
|
|
3885
|
+
keyword: error.keyword,
|
|
3886
|
+
params: error.params,
|
|
3887
|
+
message: fallbackMessage,
|
|
3888
|
+
};
|
|
3889
|
+
}
|
|
3890
|
+
}
|
|
3891
|
+
}
|
|
3892
|
+
});
|
|
3893
|
+
return fieldErrors;
|
|
3894
|
+
};
|
|
3895
|
+
/**
|
|
3896
|
+
* AJV resolver for react-hook-form
|
|
3897
|
+
* Integrates AJV validation with react-hook-form's validation system
|
|
3898
|
+
*/
|
|
3899
|
+
/**
|
|
3900
|
+
* Strips null, undefined, and empty string values from an object
|
|
3901
|
+
*/
|
|
3902
|
+
const stripEmptyValues = (obj) => {
|
|
3903
|
+
if (obj === null || obj === undefined) {
|
|
3904
|
+
return undefined;
|
|
3905
|
+
}
|
|
3906
|
+
if (typeof obj === 'string' && obj.trim() === '') {
|
|
3907
|
+
return undefined;
|
|
3908
|
+
}
|
|
3909
|
+
if (Array.isArray(obj)) {
|
|
3910
|
+
const filtered = obj
|
|
3911
|
+
.map(stripEmptyValues)
|
|
3912
|
+
.filter((item) => item !== undefined);
|
|
3913
|
+
return filtered.length > 0 ? filtered : undefined;
|
|
3914
|
+
}
|
|
3915
|
+
if (typeof obj === 'object' && obj !== null) {
|
|
3916
|
+
const result = {};
|
|
3917
|
+
let hasValues = false;
|
|
3918
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
3919
|
+
const cleanedValue = stripEmptyValues(value);
|
|
3920
|
+
if (cleanedValue !== undefined) {
|
|
3921
|
+
result[key] = cleanedValue;
|
|
3922
|
+
hasValues = true;
|
|
3923
|
+
}
|
|
3924
|
+
}
|
|
3925
|
+
return hasValues ? result : undefined;
|
|
3926
|
+
}
|
|
3927
|
+
return obj;
|
|
3928
|
+
};
|
|
3929
|
+
const ajvResolver = (schema) => {
|
|
3930
|
+
return async (values) => {
|
|
3931
|
+
try {
|
|
3932
|
+
// Strip empty values before validation
|
|
3933
|
+
const cleanedValues = stripEmptyValues(values);
|
|
3934
|
+
// Use empty object for validation if all values were stripped
|
|
3935
|
+
const valuesToValidate = cleanedValues === undefined ? {} : cleanedValues;
|
|
3936
|
+
const { isValid, errors } = validateData(valuesToValidate, schema);
|
|
3937
|
+
console.debug('AJV Validation Result:', {
|
|
3938
|
+
isValid,
|
|
3939
|
+
errors,
|
|
3940
|
+
cleanedValues,
|
|
3941
|
+
valuesToValidate,
|
|
3942
|
+
});
|
|
3943
|
+
if (isValid) {
|
|
3944
|
+
return {
|
|
3945
|
+
values: (cleanedValues || {}),
|
|
3946
|
+
errors: {},
|
|
3947
|
+
};
|
|
3948
|
+
}
|
|
3949
|
+
const fieldErrors = convertAjvErrorsToFieldErrors(errors, schema);
|
|
3950
|
+
console.debug('AJV Validation Failed:', {
|
|
3951
|
+
errors,
|
|
3952
|
+
fieldErrors,
|
|
3953
|
+
cleanedValues,
|
|
3954
|
+
valuesToValidate,
|
|
3955
|
+
});
|
|
3956
|
+
return {
|
|
3957
|
+
values: {},
|
|
3958
|
+
errors: fieldErrors,
|
|
3959
|
+
};
|
|
3960
|
+
}
|
|
3961
|
+
catch (error) {
|
|
3962
|
+
return {
|
|
3963
|
+
values: {},
|
|
3964
|
+
errors: {
|
|
3965
|
+
root: {
|
|
3966
|
+
type: 'validation',
|
|
3967
|
+
message: error instanceof Error ? error.message : 'Validation failed',
|
|
3968
|
+
},
|
|
3969
|
+
},
|
|
3970
|
+
};
|
|
3971
|
+
}
|
|
3972
|
+
};
|
|
3973
|
+
};
|
|
3974
|
+
|
|
3598
3975
|
const idPickerSanityCheck = (column, foreign_key) => {
|
|
3599
3976
|
if (!!foreign_key == false) {
|
|
3600
3977
|
throw new Error(`The key foreign_key does not exist in properties of column ${column} when using id-picker.`);
|
|
@@ -3610,13 +3987,65 @@ const idPickerSanityCheck = (column, foreign_key) => {
|
|
|
3610
3987
|
throw new Error(`The key column does not exist in properties of column ${column} when using id-picker.`);
|
|
3611
3988
|
}
|
|
3612
3989
|
};
|
|
3613
|
-
const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, children, order = [], ignore = [], include = [], onSubmit = undefined, rowNumber = undefined, requestOptions = {}, getUpdatedData = () => { },
|
|
3990
|
+
const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, children, order = [], ignore = [], include = [], onSubmit = undefined, rowNumber = undefined, requestOptions = {}, getUpdatedData = () => { }, customErrorRenderer, customSuccessRenderer, displayConfig = {
|
|
3991
|
+
showSubmitButton: true,
|
|
3992
|
+
showResetButton: true,
|
|
3993
|
+
showTitle: true,
|
|
3994
|
+
}, requireConfirmation = false, dateTimePickerLabels, idPickerLabels, enumPickerLabels, filePickerLabels, }) => {
|
|
3614
3995
|
const [isSuccess, setIsSuccess] = React.useState(false);
|
|
3615
3996
|
const [isError, setIsError] = React.useState(false);
|
|
3616
3997
|
const [isSubmiting, setIsSubmiting] = React.useState(false);
|
|
3617
3998
|
const [isConfirming, setIsConfirming] = React.useState(false);
|
|
3618
3999
|
const [validatedData, setValidatedData] = React.useState();
|
|
3619
4000
|
const [error, setError] = React.useState();
|
|
4001
|
+
const onBeforeSubmit = () => {
|
|
4002
|
+
setIsSubmiting(true);
|
|
4003
|
+
};
|
|
4004
|
+
const onAfterSubmit = () => {
|
|
4005
|
+
setIsSubmiting(false);
|
|
4006
|
+
};
|
|
4007
|
+
const onSubmitError = (error) => {
|
|
4008
|
+
setIsError(true);
|
|
4009
|
+
setError(error);
|
|
4010
|
+
};
|
|
4011
|
+
const onSubmitSuccess = () => {
|
|
4012
|
+
setIsSuccess(true);
|
|
4013
|
+
};
|
|
4014
|
+
const defaultOnSubmit = async (promise) => {
|
|
4015
|
+
try {
|
|
4016
|
+
console.log('onBeforeSubmit');
|
|
4017
|
+
onBeforeSubmit();
|
|
4018
|
+
await promise;
|
|
4019
|
+
console.log('onSubmitSuccess');
|
|
4020
|
+
onSubmitSuccess();
|
|
4021
|
+
}
|
|
4022
|
+
catch (error) {
|
|
4023
|
+
console.log('onSubmitError', error);
|
|
4024
|
+
onSubmitError(error);
|
|
4025
|
+
}
|
|
4026
|
+
finally {
|
|
4027
|
+
onAfterSubmit();
|
|
4028
|
+
}
|
|
4029
|
+
};
|
|
4030
|
+
const defaultSubmitPromise = (data) => {
|
|
4031
|
+
const options = {
|
|
4032
|
+
method: 'POST',
|
|
4033
|
+
url: `${serverUrl}`,
|
|
4034
|
+
data: clearEmptyString(data),
|
|
4035
|
+
...requestOptions,
|
|
4036
|
+
};
|
|
4037
|
+
return axios.request(options);
|
|
4038
|
+
};
|
|
4039
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4040
|
+
const onFormSubmit = async (data) => {
|
|
4041
|
+
// AJV validation is now handled by react-hook-form resolver
|
|
4042
|
+
// This function will only be called if validation passes
|
|
4043
|
+
if (onSubmit === undefined) {
|
|
4044
|
+
await defaultOnSubmit(Promise.resolve(defaultSubmitPromise(data)));
|
|
4045
|
+
return;
|
|
4046
|
+
}
|
|
4047
|
+
await defaultOnSubmit(Promise.resolve(onSubmit(data)));
|
|
4048
|
+
};
|
|
3620
4049
|
return (jsxRuntime.jsx(SchemaFormContext.Provider, { value: {
|
|
3621
4050
|
schema,
|
|
3622
4051
|
serverUrl,
|
|
@@ -3643,11 +4072,21 @@ const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, childre
|
|
|
3643
4072
|
error,
|
|
3644
4073
|
setError,
|
|
3645
4074
|
getUpdatedData,
|
|
4075
|
+
customErrorRenderer,
|
|
4076
|
+
customSuccessRenderer,
|
|
4077
|
+
displayConfig,
|
|
4078
|
+
requireConfirmation,
|
|
4079
|
+
onFormSubmit,
|
|
4080
|
+
dateTimePickerLabels,
|
|
4081
|
+
idPickerLabels,
|
|
4082
|
+
enumPickerLabels,
|
|
4083
|
+
filePickerLabels,
|
|
4084
|
+
ajvResolver: ajvResolver(schema),
|
|
3646
4085
|
}, children: jsxRuntime.jsx(reactHookForm.FormProvider, { ...form, children: children }) }));
|
|
3647
4086
|
};
|
|
3648
4087
|
|
|
3649
4088
|
function removeIndex(str) {
|
|
3650
|
-
return str.replace(/\.\d+\./g,
|
|
4089
|
+
return str.replace(/\.\d+\./g, ".");
|
|
3651
4090
|
}
|
|
3652
4091
|
|
|
3653
4092
|
const ArrayRenderer = ({ schema, column, prefix, }) => {
|
|
@@ -3659,13 +4098,17 @@ const ArrayRenderer = ({ schema, column, prefix, }) => {
|
|
|
3659
4098
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
3660
4099
|
const { formState: { errors }, setValue, watch, } = reactHookForm.useFormContext();
|
|
3661
4100
|
const fields = (watch(colLabel) ?? []);
|
|
3662
|
-
return (jsxRuntime.jsxs(react.
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
4101
|
+
return (jsxRuntime.jsxs(react.Flex, { gridRow, gridColumn, flexFlow: "column", gap: 2, children: [jsxRuntime.jsxs(react.Box, { as: "label", children: [`${translate.t(removeIndex(`${colLabel}.field_label`))}`, isRequired && jsxRuntime.jsx("span", { children: "*" })] }), jsxRuntime.jsx(react.Flex, { flexFlow: "column", gap: 2, children: fields.map((field, index) => (jsxRuntime.jsxs(react.Grid, { gridTemplateColumns: '1fr auto', gap: 2, bgColor: { base: "colorPalette.100", _dark: "colorPalette.900" }, p: 2, borderRadius: 4, borderWidth: 1, borderColor: {
|
|
4102
|
+
base: "colorPalette.200",
|
|
4103
|
+
_dark: "colorPalette.800",
|
|
4104
|
+
}, children: [jsxRuntime.jsx(react.Grid, { gridTemplateColumns: "repeat(12, 1fr)", autoFlow: "row", children: jsxRuntime.jsx(SchemaRenderer, { column: `${index}`,
|
|
4105
|
+
prefix: `${colLabel}.`,
|
|
4106
|
+
// @ts-expect-error find suitable types
|
|
4107
|
+
schema: { showLabel: false, ...(items ?? {}) } }) }), jsxRuntime.jsx(react.Flex, { justifyContent: "end", children: jsxRuntime.jsx(react.Button, { variant: "ghost", onClick: () => {
|
|
4108
|
+
setValue(colLabel, fields.filter((_, curIndex) => {
|
|
4109
|
+
return curIndex !== index;
|
|
4110
|
+
}));
|
|
4111
|
+
}, children: jsxRuntime.jsx(react.Icon, { children: jsxRuntime.jsx(cg.CgTrash, {}) }) }) })] }, `${colLabel}.${index}`))) }), jsxRuntime.jsx(react.Flex, { children: jsxRuntime.jsx(react.Button, { onClick: () => {
|
|
3669
4112
|
if (type === "number") {
|
|
3670
4113
|
setValue(colLabel, [...fields, 0]);
|
|
3671
4114
|
return;
|
|
@@ -3679,48 +4122,49 @@ const ArrayRenderer = ({ schema, column, prefix, }) => {
|
|
|
3679
4122
|
return;
|
|
3680
4123
|
}
|
|
3681
4124
|
setValue(colLabel, [...fields, {}]);
|
|
3682
|
-
}, children: translate.t(removeIndex(`${colLabel}.add`)) }) }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.
|
|
4125
|
+
}, children: translate.t(removeIndex(`${colLabel}.add`)) }) }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
|
|
3683
4126
|
};
|
|
3684
4127
|
|
|
3685
4128
|
const Field = React__namespace.forwardRef(function Field(props, ref) {
|
|
3686
4129
|
const { label, children, helperText, errorText, optionalText, ...rest } = props;
|
|
3687
|
-
return (jsxRuntime.jsxs(react.Field.Root, { ref: ref, ...rest, children: [label && (jsxRuntime.jsxs(react.Field.Label, { children: [label, jsxRuntime.jsx(react.Field.RequiredIndicator, { fallback: optionalText })] })), children, helperText && (jsxRuntime.jsx(react.Field.HelperText, { children: helperText })), errorText && (jsxRuntime.
|
|
4130
|
+
return (jsxRuntime.jsxs(react.Field.Root, { ref: ref, ...rest, children: [label && (jsxRuntime.jsxs(react.Field.Label, { children: [label, jsxRuntime.jsx(react.Field.RequiredIndicator, { color: rest.invalid && rest.required ? 'red.500' : undefined, fallback: optionalText })] })), children, helperText && (jsxRuntime.jsx(react.Field.HelperText, { children: helperText })), !!errorText && (jsxRuntime.jsxs(react.Field.ErrorText, { children: [rest.required && rest.invalid && (jsxRuntime.jsx("span", { style: { color: 'var(--chakra-colors-red-500)' }, children: "* " })), errorText] }))] }));
|
|
3688
4131
|
});
|
|
3689
4132
|
|
|
3690
4133
|
const BooleanPicker = ({ schema, column, prefix }) => {
|
|
3691
4134
|
const { watch, formState: { errors }, setValue, } = reactHookForm.useFormContext();
|
|
3692
4135
|
const { translate } = useSchemaContext();
|
|
3693
|
-
const { required, gridColumn, gridRow } = schema;
|
|
4136
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1' } = schema;
|
|
3694
4137
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
3695
4138
|
const colLabel = `${prefix}${column}`;
|
|
3696
4139
|
const value = watch(colLabel);
|
|
3697
|
-
return (jsxRuntime.
|
|
3698
|
-
gridRow,
|
|
3699
|
-
|
|
3700
|
-
|
|
4140
|
+
return (jsxRuntime.jsx(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, alignItems: 'stretch', gridColumn,
|
|
4141
|
+
gridRow, errorText: errors[`${colLabel}`]
|
|
4142
|
+
? translate.t(removeIndex(`${colLabel}.field_required`))
|
|
4143
|
+
: undefined, invalid: !!errors[colLabel], children: jsxRuntime.jsx(CheckboxCard, { checked: value, variant: 'surface', onChange: () => {
|
|
4144
|
+
setValue(colLabel, !value);
|
|
4145
|
+
} }) }));
|
|
4146
|
+
};
|
|
4147
|
+
|
|
4148
|
+
const CustomInput = ({ column, schema, prefix }) => {
|
|
4149
|
+
const formContext = reactHookForm.useFormContext();
|
|
4150
|
+
const { inputRender } = schema;
|
|
4151
|
+
return (inputRender &&
|
|
4152
|
+
inputRender({
|
|
4153
|
+
column,
|
|
4154
|
+
schema,
|
|
4155
|
+
prefix,
|
|
4156
|
+
formContext,
|
|
4157
|
+
}));
|
|
3701
4158
|
};
|
|
3702
4159
|
|
|
3703
|
-
const monthNamesShort = [
|
|
3704
|
-
"Jan",
|
|
3705
|
-
"Feb",
|
|
3706
|
-
"Mar",
|
|
3707
|
-
"Apr",
|
|
3708
|
-
"May",
|
|
3709
|
-
"Jun",
|
|
3710
|
-
"Jul",
|
|
3711
|
-
"Aug",
|
|
3712
|
-
"Sep",
|
|
3713
|
-
"Oct",
|
|
3714
|
-
"Nov",
|
|
3715
|
-
"Dec",
|
|
3716
|
-
];
|
|
3717
|
-
const weekdayNamesShort = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
3718
4160
|
const Calendar = ({ calendars, getBackProps, getForwardProps, getDateProps, firstDayOfWeek = 0, }) => {
|
|
4161
|
+
const { labels } = React.useContext(DatePickerContext);
|
|
4162
|
+
const { monthNamesShort, weekdayNamesShort, backButtonLabel, forwardButtonLabel } = labels;
|
|
3719
4163
|
if (calendars.length) {
|
|
3720
4164
|
return (jsxRuntime.jsxs(react.Grid, { children: [jsxRuntime.jsxs(react.Grid, { templateColumns: "repeat(4, auto)", justifyContent: "center", children: [jsxRuntime.jsx(react.Button, { variant: "ghost", ...getBackProps({
|
|
3721
4165
|
calendars,
|
|
3722
4166
|
offset: 12,
|
|
3723
|
-
}), children: "<<" }), jsxRuntime.jsx(react.Button, { variant: "ghost", ...getBackProps({ calendars }), children:
|
|
4167
|
+
}), children: "<<" }), jsxRuntime.jsx(react.Button, { variant: "ghost", ...getBackProps({ calendars }), children: backButtonLabel }), jsxRuntime.jsx(react.Button, { variant: "ghost", ...getForwardProps({ calendars }), children: forwardButtonLabel }), jsxRuntime.jsx(react.Button, { variant: "ghost", ...getForwardProps({
|
|
3724
4168
|
calendars,
|
|
3725
4169
|
offset: 12,
|
|
3726
4170
|
}), children: ">>" })] }), jsxRuntime.jsx(react.Grid, { templateColumns: "repeat(2, auto)", justifyContent: "center", children: calendars.map((calendar) => (jsxRuntime.jsxs(react.Grid, { gap: 4, children: [jsxRuntime.jsxs(react.Grid, { justifyContent: "center", children: [monthNamesShort[calendar.month], " ", calendar.year] }), jsxRuntime.jsxs(react.Grid, { templateColumns: "repeat(7, auto)", justifyContent: "center", children: [[0, 1, 2, 3, 4, 5, 6].map((weekdayNum) => {
|
|
@@ -3763,9 +4207,52 @@ const Calendar = ({ calendars, getBackProps, getForwardProps, getDateProps, firs
|
|
|
3763
4207
|
}
|
|
3764
4208
|
return null;
|
|
3765
4209
|
};
|
|
4210
|
+
const DatePickerContext = React.createContext({
|
|
4211
|
+
labels: {
|
|
4212
|
+
monthNamesShort: [
|
|
4213
|
+
"Jan",
|
|
4214
|
+
"Feb",
|
|
4215
|
+
"Mar",
|
|
4216
|
+
"Apr",
|
|
4217
|
+
"May",
|
|
4218
|
+
"Jun",
|
|
4219
|
+
"Jul",
|
|
4220
|
+
"Aug",
|
|
4221
|
+
"Sep",
|
|
4222
|
+
"Oct",
|
|
4223
|
+
"Nov",
|
|
4224
|
+
"Dec",
|
|
4225
|
+
],
|
|
4226
|
+
weekdayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
|
|
4227
|
+
backButtonLabel: "Back",
|
|
4228
|
+
forwardButtonLabel: "Next",
|
|
4229
|
+
},
|
|
4230
|
+
});
|
|
3766
4231
|
let DatePicker$1 = class DatePicker extends React.Component {
|
|
3767
4232
|
render() {
|
|
3768
|
-
|
|
4233
|
+
const { labels = {
|
|
4234
|
+
monthNamesShort: [
|
|
4235
|
+
"Jan",
|
|
4236
|
+
"Feb",
|
|
4237
|
+
"Mar",
|
|
4238
|
+
"Apr",
|
|
4239
|
+
"May",
|
|
4240
|
+
"Jun",
|
|
4241
|
+
"Jul",
|
|
4242
|
+
"Aug",
|
|
4243
|
+
"Sep",
|
|
4244
|
+
"Oct",
|
|
4245
|
+
"Nov",
|
|
4246
|
+
"Dec",
|
|
4247
|
+
],
|
|
4248
|
+
weekdayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
|
|
4249
|
+
backButtonLabel: "Back",
|
|
4250
|
+
forwardButtonLabel: "Next",
|
|
4251
|
+
}, } = this.props;
|
|
4252
|
+
return (jsxRuntime.jsx(DatePickerContext.Provider, { value: { labels }, children: jsxRuntime.jsx(Dayzed, { onDateSelected: this.props.onDateSelected, selected: this.props.selected, firstDayOfWeek: this.props.firstDayOfWeek, showOutsideDays: this.props.showOutsideDays, date: this.props.date, minDate: this.props.minDate, maxDate: this.props.maxDate, monthsToDisplay: this.props.monthsToDisplay, render:
|
|
4253
|
+
// @ts-expect-error - Dayzed types need to be fixed
|
|
4254
|
+
(dayzedData) => (jsxRuntime.jsx(Calendar, { ...dayzedData,
|
|
4255
|
+
firstDayOfWeek: this.props.firstDayOfWeek })) }) }));
|
|
3769
4256
|
}
|
|
3770
4257
|
};
|
|
3771
4258
|
|
|
@@ -3787,28 +4274,270 @@ const PopoverRoot = react.Popover.Root;
|
|
|
3787
4274
|
const PopoverBody = react.Popover.Body;
|
|
3788
4275
|
const PopoverTrigger = react.Popover.Trigger;
|
|
3789
4276
|
|
|
4277
|
+
/**
|
|
4278
|
+
* Custom hook to simplify i18n translation for form fields.
|
|
4279
|
+
* Automatically handles colLabel construction and removeIndex logic.
|
|
4280
|
+
*
|
|
4281
|
+
* @param column - The column name
|
|
4282
|
+
* @param prefix - The prefix for the field (usually empty string or parent path)
|
|
4283
|
+
* @returns Object with translation helper functions
|
|
4284
|
+
*
|
|
4285
|
+
* @example
|
|
4286
|
+
* ```tsx
|
|
4287
|
+
* const formI18n = useFormI18n(column, prefix);
|
|
4288
|
+
*
|
|
4289
|
+
* // Get field label
|
|
4290
|
+
* <Field label={formI18n.label()} />
|
|
4291
|
+
*
|
|
4292
|
+
* // Get error message
|
|
4293
|
+
* <Text>{formI18n.required()}</Text>
|
|
4294
|
+
*
|
|
4295
|
+
* // Get custom translation key
|
|
4296
|
+
* <Text>{formI18n.t('add_more')}</Text>
|
|
4297
|
+
*
|
|
4298
|
+
* // Access the raw colLabel
|
|
4299
|
+
* const colLabel = formI18n.colLabel;
|
|
4300
|
+
* ```
|
|
4301
|
+
*/
|
|
4302
|
+
const useFormI18n = (column, prefix = "") => {
|
|
4303
|
+
const { translate } = useSchemaContext();
|
|
4304
|
+
const colLabel = `${prefix}${column}`;
|
|
4305
|
+
return {
|
|
4306
|
+
/**
|
|
4307
|
+
* The constructed column label (prefix + column)
|
|
4308
|
+
*/
|
|
4309
|
+
colLabel,
|
|
4310
|
+
/**
|
|
4311
|
+
* Get the field label translation
|
|
4312
|
+
* Equivalent to: translate.t(removeIndex(`${colLabel}.field_label`))
|
|
4313
|
+
*/
|
|
4314
|
+
label: (options) => {
|
|
4315
|
+
return translate.t(removeIndex(`${colLabel}.field_label`), options);
|
|
4316
|
+
},
|
|
4317
|
+
/**
|
|
4318
|
+
* Get the required error message translation
|
|
4319
|
+
* Equivalent to: translate.t(removeIndex(`${colLabel}.field_required`))
|
|
4320
|
+
*/
|
|
4321
|
+
required: (options) => {
|
|
4322
|
+
return translate.t(removeIndex(`${colLabel}.field_required`), options);
|
|
4323
|
+
},
|
|
4324
|
+
/**
|
|
4325
|
+
* Get a translation for any custom key relative to the field
|
|
4326
|
+
* Equivalent to: translate.t(removeIndex(`${colLabel}.${key}`))
|
|
4327
|
+
*
|
|
4328
|
+
* @param key - The translation key suffix (e.g., 'add_more', 'total', etc.)
|
|
4329
|
+
* @param options - Optional translation options (e.g., defaultValue, interpolation variables)
|
|
4330
|
+
*/
|
|
4331
|
+
t: (key, options) => {
|
|
4332
|
+
return translate.t(removeIndex(`${colLabel}.${key}`), options);
|
|
4333
|
+
},
|
|
4334
|
+
/**
|
|
4335
|
+
* Access to the original translate object for edge cases
|
|
4336
|
+
*/
|
|
4337
|
+
translate,
|
|
4338
|
+
};
|
|
4339
|
+
};
|
|
4340
|
+
|
|
4341
|
+
dayjs.extend(utc);
|
|
4342
|
+
dayjs.extend(timezone);
|
|
3790
4343
|
const DatePicker = ({ column, schema, prefix }) => {
|
|
3791
4344
|
const { watch, formState: { errors }, setValue, } = reactHookForm.useFormContext();
|
|
3792
|
-
const {
|
|
3793
|
-
const
|
|
4345
|
+
const { timezone, dateTimePickerLabels } = useSchemaContext();
|
|
4346
|
+
const formI18n = useFormI18n(column, prefix);
|
|
4347
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1', displayDateFormat = 'YYYY-MM-DD', dateFormat = 'YYYY-MM-DD', } = schema;
|
|
3794
4348
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
3795
|
-
const colLabel =
|
|
4349
|
+
const colLabel = formI18n.colLabel;
|
|
3796
4350
|
const [open, setOpen] = React.useState(false);
|
|
3797
4351
|
const selectedDate = watch(colLabel);
|
|
3798
|
-
const
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
4352
|
+
const displayDate = dayjs(selectedDate)
|
|
4353
|
+
.tz(timezone)
|
|
4354
|
+
.format(displayDateFormat);
|
|
4355
|
+
React.useEffect(() => {
|
|
4356
|
+
try {
|
|
4357
|
+
if (selectedDate) {
|
|
4358
|
+
// Parse the selectedDate as UTC or in a specific timezone to avoid +8 hour shift
|
|
4359
|
+
// For example, parse as UTC:
|
|
4360
|
+
const parsedDate = dayjs(selectedDate).tz(timezone);
|
|
4361
|
+
if (!parsedDate.isValid())
|
|
4362
|
+
return;
|
|
4363
|
+
// Format according to dateFormat from schema
|
|
4364
|
+
const formatted = parsedDate.format(dateFormat);
|
|
4365
|
+
// Update the form value only if different to avoid loops
|
|
4366
|
+
if (formatted !== selectedDate) {
|
|
4367
|
+
setValue(colLabel, formatted, {
|
|
4368
|
+
shouldValidate: true,
|
|
4369
|
+
shouldDirty: true,
|
|
4370
|
+
});
|
|
4371
|
+
}
|
|
4372
|
+
}
|
|
4373
|
+
}
|
|
4374
|
+
catch (e) {
|
|
4375
|
+
console.error(e);
|
|
4376
|
+
}
|
|
4377
|
+
}, [selectedDate, dateFormat, colLabel, setValue]);
|
|
4378
|
+
return (jsxRuntime.jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
4379
|
+
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: jsxRuntime.jsxs(PopoverRoot, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, children: [jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: jsxRuntime.jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
|
|
4380
|
+
setOpen(true);
|
|
4381
|
+
}, justifyContent: 'start', children: [jsxRuntime.jsx(md.MdDateRange, {}), selectedDate !== undefined ? `${displayDate}` : ''] }) }), jsxRuntime.jsx(PopoverContent, { children: jsxRuntime.jsxs(PopoverBody, { children: [jsxRuntime.jsx(PopoverTitle, {}), jsxRuntime.jsx(DatePicker$1, { selected: new Date(selectedDate), onDateSelected: ({ date }) => {
|
|
4382
|
+
setValue(colLabel, dayjs(date).format(dateFormat));
|
|
4383
|
+
setOpen(false);
|
|
4384
|
+
}, labels: {
|
|
4385
|
+
monthNamesShort: dateTimePickerLabels?.monthNamesShort ?? [
|
|
4386
|
+
formI18n.translate.t(`common.month_1`, {
|
|
4387
|
+
defaultValue: 'January',
|
|
4388
|
+
}),
|
|
4389
|
+
formI18n.translate.t(`common.month_2`, {
|
|
4390
|
+
defaultValue: 'February',
|
|
4391
|
+
}),
|
|
4392
|
+
formI18n.translate.t(`common.month_3`, {
|
|
4393
|
+
defaultValue: 'March',
|
|
4394
|
+
}),
|
|
4395
|
+
formI18n.translate.t(`common.month_4`, {
|
|
4396
|
+
defaultValue: 'April',
|
|
4397
|
+
}),
|
|
4398
|
+
formI18n.translate.t(`common.month_5`, {
|
|
4399
|
+
defaultValue: 'May',
|
|
4400
|
+
}),
|
|
4401
|
+
formI18n.translate.t(`common.month_6`, {
|
|
4402
|
+
defaultValue: 'June',
|
|
4403
|
+
}),
|
|
4404
|
+
formI18n.translate.t(`common.month_7`, {
|
|
4405
|
+
defaultValue: 'July',
|
|
4406
|
+
}),
|
|
4407
|
+
formI18n.translate.t(`common.month_8`, {
|
|
4408
|
+
defaultValue: 'August',
|
|
4409
|
+
}),
|
|
4410
|
+
formI18n.translate.t(`common.month_9`, {
|
|
4411
|
+
defaultValue: 'September',
|
|
4412
|
+
}),
|
|
4413
|
+
formI18n.translate.t(`common.month_10`, {
|
|
4414
|
+
defaultValue: 'October',
|
|
4415
|
+
}),
|
|
4416
|
+
formI18n.translate.t(`common.month_11`, {
|
|
4417
|
+
defaultValue: 'November',
|
|
4418
|
+
}),
|
|
4419
|
+
formI18n.translate.t(`common.month_12`, {
|
|
4420
|
+
defaultValue: 'December',
|
|
4421
|
+
}),
|
|
4422
|
+
],
|
|
4423
|
+
weekdayNamesShort: dateTimePickerLabels?.weekdayNamesShort ?? [
|
|
4424
|
+
formI18n.translate.t(`common.weekday_1`, {
|
|
4425
|
+
defaultValue: 'Sun',
|
|
4426
|
+
}),
|
|
4427
|
+
formI18n.translate.t(`common.weekday_2`, {
|
|
4428
|
+
defaultValue: 'Mon',
|
|
4429
|
+
}),
|
|
4430
|
+
formI18n.translate.t(`common.weekday_3`, {
|
|
4431
|
+
defaultValue: 'Tue',
|
|
4432
|
+
}),
|
|
4433
|
+
formI18n.translate.t(`common.weekday_4`, {
|
|
4434
|
+
defaultValue: 'Wed',
|
|
4435
|
+
}),
|
|
4436
|
+
formI18n.translate.t(`common.weekday_5`, {
|
|
4437
|
+
defaultValue: 'Thu',
|
|
4438
|
+
}),
|
|
4439
|
+
formI18n.translate.t(`common.weekday_6`, {
|
|
4440
|
+
defaultValue: 'Fri',
|
|
4441
|
+
}),
|
|
4442
|
+
formI18n.translate.t(`common.weekday_7`, {
|
|
4443
|
+
defaultValue: 'Sat',
|
|
4444
|
+
}),
|
|
4445
|
+
],
|
|
4446
|
+
backButtonLabel: dateTimePickerLabels?.backButtonLabel ??
|
|
4447
|
+
formI18n.translate.t(`common.back_button`, {
|
|
4448
|
+
defaultValue: 'Back',
|
|
4449
|
+
}),
|
|
4450
|
+
forwardButtonLabel: dateTimePickerLabels?.forwardButtonLabel ??
|
|
4451
|
+
formI18n.translate.t(`common.forward_button`, {
|
|
4452
|
+
defaultValue: 'Forward',
|
|
4453
|
+
}),
|
|
4454
|
+
} })] }) })] }) }));
|
|
4455
|
+
};
|
|
4456
|
+
|
|
4457
|
+
dayjs.extend(utc);
|
|
4458
|
+
dayjs.extend(timezone);
|
|
4459
|
+
const DateRangePicker = ({ column, schema, prefix, }) => {
|
|
4460
|
+
const { watch, formState: { errors }, setValue, } = reactHookForm.useFormContext();
|
|
4461
|
+
const { timezone, dateTimePickerLabels } = useSchemaContext();
|
|
4462
|
+
const formI18n = useFormI18n(column, prefix);
|
|
4463
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1', displayDateFormat = 'YYYY-MM-DD', dateFormat = 'YYYY-MM-DD', } = schema;
|
|
4464
|
+
const isRequired = required?.some((columnId) => columnId === column);
|
|
4465
|
+
const colLabel = formI18n.colLabel;
|
|
4466
|
+
const [open, setOpen] = React.useState(false);
|
|
4467
|
+
const selectedDateRange = watch(colLabel);
|
|
4468
|
+
// Convert string[] to Date[] for the picker
|
|
4469
|
+
const selectedDates = (selectedDateRange ?? [])
|
|
4470
|
+
.map((dateStr) => {
|
|
4471
|
+
if (!dateStr)
|
|
4472
|
+
return null;
|
|
4473
|
+
const parsed = dayjs(dateStr).tz(timezone);
|
|
4474
|
+
return parsed.isValid() ? parsed.toDate() : null;
|
|
4475
|
+
})
|
|
4476
|
+
.filter((date) => date !== null);
|
|
4477
|
+
// Format display string
|
|
4478
|
+
const getDisplayText = () => {
|
|
4479
|
+
if (!selectedDateRange || selectedDateRange.length === 0) {
|
|
4480
|
+
return '';
|
|
4481
|
+
}
|
|
4482
|
+
if (selectedDateRange.length === 1) {
|
|
4483
|
+
const date = dayjs(selectedDateRange[0]).tz(timezone);
|
|
4484
|
+
return date.isValid() ? date.format(displayDateFormat) : '';
|
|
4485
|
+
}
|
|
4486
|
+
if (selectedDateRange.length === 2) {
|
|
4487
|
+
const startDate = dayjs(selectedDateRange[0]).tz(timezone);
|
|
4488
|
+
const endDate = dayjs(selectedDateRange[1]).tz(timezone);
|
|
4489
|
+
if (startDate.isValid() && endDate.isValid()) {
|
|
4490
|
+
return `${startDate.format(displayDateFormat)} - ${endDate.format(displayDateFormat)}`;
|
|
4491
|
+
}
|
|
4492
|
+
}
|
|
4493
|
+
return '';
|
|
4494
|
+
};
|
|
4495
|
+
React.useEffect(() => {
|
|
4496
|
+
try {
|
|
4497
|
+
if (selectedDateRange && selectedDateRange.length > 0) {
|
|
4498
|
+
// Format dates according to dateFormat from schema
|
|
4499
|
+
const formatted = selectedDateRange
|
|
4500
|
+
.map((dateStr) => {
|
|
4501
|
+
if (!dateStr)
|
|
4502
|
+
return null;
|
|
4503
|
+
const parsed = dayjs(dateStr).tz(timezone);
|
|
4504
|
+
return parsed.isValid() ? parsed.format(dateFormat) : null;
|
|
4505
|
+
})
|
|
4506
|
+
.filter((date) => date !== null);
|
|
4507
|
+
// Update the form value only if different to avoid loops
|
|
4508
|
+
// Compare arrays element by element
|
|
4509
|
+
const needsUpdate = formatted.length !== selectedDateRange.length ||
|
|
4510
|
+
formatted.some((val, idx) => val !== selectedDateRange[idx]);
|
|
4511
|
+
if (needsUpdate && formatted.length > 0) {
|
|
4512
|
+
setValue(colLabel, formatted, {
|
|
4513
|
+
shouldValidate: true,
|
|
4514
|
+
shouldDirty: true,
|
|
4515
|
+
});
|
|
4516
|
+
}
|
|
4517
|
+
}
|
|
4518
|
+
}
|
|
4519
|
+
catch (e) {
|
|
4520
|
+
console.error(e);
|
|
4521
|
+
}
|
|
4522
|
+
}, [selectedDateRange, dateFormat, colLabel, setValue, timezone]);
|
|
4523
|
+
return (jsxRuntime.jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
4524
|
+
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: jsxRuntime.jsxs(PopoverRoot, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, children: [jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: jsxRuntime.jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
|
|
4525
|
+
setOpen(true);
|
|
4526
|
+
}, justifyContent: 'start', children: [jsxRuntime.jsx(md.MdDateRange, {}), getDisplayText()] }) }), jsxRuntime.jsx(PopoverContent, { minW: '600px', children: jsxRuntime.jsxs(PopoverBody, { children: [jsxRuntime.jsx(PopoverTitle, {}), jsxRuntime.jsx(RangeDatePicker, { selected: selectedDates, onDateSelected: ({ selected, selectable, date }) => {
|
|
4527
|
+
const newDates = getRangeDates({
|
|
4528
|
+
selectable,
|
|
4529
|
+
date,
|
|
4530
|
+
selectedDates,
|
|
4531
|
+
}) ?? [];
|
|
4532
|
+
// Convert Date[] to string[]
|
|
4533
|
+
const formattedDates = newDates
|
|
4534
|
+
.map((dateObj) => dayjs(dateObj).tz(timezone).format(dateFormat))
|
|
4535
|
+
.filter((dateStr) => dateStr);
|
|
4536
|
+
setValue(colLabel, formattedDates, {
|
|
4537
|
+
shouldValidate: true,
|
|
4538
|
+
shouldDirty: true,
|
|
4539
|
+
});
|
|
4540
|
+
}, monthsToDisplay: 2 })] }) })] }) }));
|
|
3812
4541
|
};
|
|
3813
4542
|
|
|
3814
4543
|
function filterArray(array, searchTerm) {
|
|
@@ -3821,17 +4550,18 @@ function filterArray(array, searchTerm) {
|
|
|
3821
4550
|
});
|
|
3822
4551
|
}
|
|
3823
4552
|
|
|
3824
|
-
const EnumPicker = ({ column, isMultiple = false, schema, prefix, }) => {
|
|
4553
|
+
const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLimit = false, }) => {
|
|
3825
4554
|
const { watch, formState: { errors }, setValue, } = reactHookForm.useFormContext();
|
|
3826
|
-
const {
|
|
3827
|
-
const
|
|
4555
|
+
const { enumPickerLabels } = useSchemaContext();
|
|
4556
|
+
const formI18n = useFormI18n(column, prefix);
|
|
4557
|
+
const { required, variant } = schema;
|
|
3828
4558
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
3829
|
-
const { gridColumn, gridRow, renderDisplay } = schema;
|
|
4559
|
+
const { gridColumn = 'span 12', gridRow = 'span 1', renderDisplay } = schema;
|
|
3830
4560
|
const [searchText, setSearchText] = React.useState();
|
|
3831
4561
|
const [limit, setLimit] = React.useState(10);
|
|
3832
4562
|
const [openSearchResult, setOpenSearchResult] = React.useState();
|
|
3833
4563
|
const ref = React.useRef(null);
|
|
3834
|
-
const colLabel =
|
|
4564
|
+
const colLabel = formI18n.colLabel;
|
|
3835
4565
|
const watchEnum = watch(colLabel);
|
|
3836
4566
|
const watchEnums = (watch(colLabel) ?? []);
|
|
3837
4567
|
const dataList = schema.enum ?? [];
|
|
@@ -3841,32 +4571,63 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, }) => {
|
|
|
3841
4571
|
setSearchText(event.target.value);
|
|
3842
4572
|
setLimit(10);
|
|
3843
4573
|
};
|
|
3844
|
-
|
|
3845
|
-
|
|
4574
|
+
if (variant === 'radio') {
|
|
4575
|
+
return (jsxRuntime.jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
4576
|
+
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: jsxRuntime.jsx(react.RadioGroup.Root, { defaultValue: "1", children: jsxRuntime.jsx(react.HStack, { gap: "6", children: filterArray(dataList, searchText ?? '').map((item) => {
|
|
4577
|
+
return (jsxRuntime.jsxs(react.RadioGroup.Item, { onClick: () => {
|
|
4578
|
+
if (!isMultiple) {
|
|
4579
|
+
setOpenSearchResult(false);
|
|
4580
|
+
setValue(colLabel, item);
|
|
4581
|
+
return;
|
|
4582
|
+
}
|
|
4583
|
+
const newSet = new Set([...(watchEnums ?? []), item]);
|
|
4584
|
+
setValue(colLabel, [...newSet]);
|
|
4585
|
+
}, value: item, children: [jsxRuntime.jsx(react.RadioGroup.ItemHiddenInput, {}), jsxRuntime.jsx(react.RadioGroup.ItemIndicator, {}), jsxRuntime.jsx(react.RadioGroup.ItemText, { children: !!renderDisplay === true
|
|
4586
|
+
? renderDisplay(item)
|
|
4587
|
+
: formI18n.t(item) })] }, `${colLabel}-${item}`));
|
|
4588
|
+
}) }) }) }));
|
|
4589
|
+
}
|
|
4590
|
+
return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
4591
|
+
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [isMultiple && (jsxRuntime.jsxs(react.Flex, { flexFlow: 'wrap', gap: 1, children: [watchEnums.map((enumValue) => {
|
|
3846
4592
|
const item = enumValue;
|
|
3847
|
-
if (item ===
|
|
3848
|
-
return jsxRuntime.jsx(jsxRuntime.Fragment, {
|
|
4593
|
+
if (!!item === false) {
|
|
4594
|
+
return jsxRuntime.jsx(jsxRuntime.Fragment, {});
|
|
3849
4595
|
}
|
|
3850
|
-
return (jsxRuntime.jsx(Tag, { closable: true, onClick: () => {
|
|
3851
|
-
// setSelectedEnums((state) => state.filter((id) => id != item));
|
|
4596
|
+
return (jsxRuntime.jsx(Tag, { size: "lg", closable: true, onClick: () => {
|
|
3852
4597
|
setValue(column, watchEnums.filter((id) => id != item));
|
|
3853
4598
|
}, children: !!renderDisplay === true
|
|
3854
4599
|
? renderDisplay(item)
|
|
3855
|
-
:
|
|
3856
|
-
}), jsxRuntime.jsx(Tag, {
|
|
4600
|
+
: formI18n.t(item) }, item));
|
|
4601
|
+
}), jsxRuntime.jsx(Tag, { size: "lg", cursor: 'pointer', onClick: () => {
|
|
3857
4602
|
setOpenSearchResult(true);
|
|
3858
|
-
}, children:
|
|
4603
|
+
}, children: enumPickerLabels?.addMore ?? formI18n.t('add_more') }, `${colLabel}-add-more-tag`)] })), !isMultiple && (jsxRuntime.jsx(Button, { variant: 'outline', onClick: () => {
|
|
3859
4604
|
setOpenSearchResult(true);
|
|
3860
|
-
}, children: watchEnum ===
|
|
3861
|
-
? ""
|
|
3862
|
-
: translate.t(removeIndex(`${colLabel}.${watchEnum}`)) })), jsxRuntime.jsxs(PopoverRoot, { open: openSearchResult, onOpenChange: (e) => setOpenSearchResult(e.open), closeOnInteractOutside: true, initialFocusEl: () => ref.current, positioning: { placement: "bottom-start" }, children: [jsxRuntime.jsx(PopoverTrigger, {}), jsxRuntime.jsx(PopoverContent, { children: jsxRuntime.jsxs(PopoverBody, { display: "grid", gap: 1, children: [jsxRuntime.jsx(react.Input, { placeholder: translate.t(`${column}.typeToSearch`), onChange: (event) => {
|
|
4605
|
+
}, justifyContent: 'start', children: !!watchEnum === false ? '' : formI18n.t(watchEnum ?? 'null') })), jsxRuntime.jsxs(PopoverRoot, { open: openSearchResult, onOpenChange: (e) => setOpenSearchResult(e.open), closeOnInteractOutside: true, initialFocusEl: () => ref.current, positioning: { placement: 'bottom-start' }, children: [jsxRuntime.jsx(PopoverTrigger, {}), jsxRuntime.jsx(PopoverContent, { portalled: false, children: jsxRuntime.jsxs(PopoverBody, { display: 'grid', gap: 1, children: [jsxRuntime.jsx(react.Input, { placeholder: enumPickerLabels?.typeToSearch ?? formI18n.t('type_to_search'), onChange: (event) => {
|
|
3863
4606
|
onSearchChange(event);
|
|
3864
4607
|
setOpenSearchResult(true);
|
|
3865
|
-
}, autoComplete: "off", ref: ref }), jsxRuntime.jsx(PopoverTitle, {}), jsxRuntime.jsx(react.Text, { children: `${
|
|
4608
|
+
}, autoComplete: "off", ref: ref }), jsxRuntime.jsx(PopoverTitle, {}), showTotalAndLimit && (jsxRuntime.jsx(react.Text, { children: `${enumPickerLabels?.total ?? formI18n.t('total')}: ${count}, ${enumPickerLabels?.showing ?? formI18n.t('showing')} ${limit}` })), jsxRuntime.jsxs(react.Grid, { overflow: 'auto', maxHeight: '20rem', children: [jsxRuntime.jsx(react.Flex, { flexFlow: 'column wrap', children: dataList
|
|
4609
|
+
.filter((item) => {
|
|
4610
|
+
const searchTerm = (searchText || '').toLowerCase();
|
|
4611
|
+
if (!searchTerm)
|
|
4612
|
+
return true;
|
|
4613
|
+
// Check if the original enum value contains the search text
|
|
4614
|
+
const enumValueMatch = item
|
|
4615
|
+
.toLowerCase()
|
|
4616
|
+
.includes(searchTerm);
|
|
4617
|
+
// Check if the display value (translation) contains the search text
|
|
4618
|
+
const displayValue = !!renderDisplay === true
|
|
4619
|
+
? renderDisplay(item)
|
|
4620
|
+
: formI18n.t(item);
|
|
4621
|
+
// Convert to string and check if it includes the search term
|
|
4622
|
+
const displayValueString = String(displayValue).toLowerCase();
|
|
4623
|
+
const displayValueMatch = displayValueString.includes(searchTerm);
|
|
4624
|
+
return enumValueMatch || displayValueMatch;
|
|
4625
|
+
})
|
|
4626
|
+
.map((item) => {
|
|
3866
4627
|
const selected = isMultiple
|
|
3867
4628
|
? watchEnums.some((enumValue) => item === enumValue)
|
|
3868
4629
|
: watchEnum == item;
|
|
3869
|
-
return (jsxRuntime.jsx(react.Box, { cursor:
|
|
4630
|
+
return (jsxRuntime.jsx(react.Box, { cursor: 'pointer', onClick: () => {
|
|
3870
4631
|
if (!isMultiple) {
|
|
3871
4632
|
setOpenSearchResult(false);
|
|
3872
4633
|
setValue(colLabel, item);
|
|
@@ -3874,10 +4635,11 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, }) => {
|
|
|
3874
4635
|
}
|
|
3875
4636
|
const newSet = new Set([...(watchEnums ?? []), item]);
|
|
3876
4637
|
setValue(colLabel, [...newSet]);
|
|
3877
|
-
}, ...(selected ? { color:
|
|
4638
|
+
}, ...(selected ? { color: 'colorPalette.400/50' } : {}), children: !!renderDisplay === true
|
|
3878
4639
|
? renderDisplay(item)
|
|
3879
|
-
:
|
|
3880
|
-
}) }), isDirty && (jsxRuntime.jsx(jsxRuntime.Fragment, { children: dataList.length <= 0 && (jsxRuntime.jsx(jsxRuntime.Fragment, { children:
|
|
4640
|
+
: formI18n.t(item) }, `${colLabel}-${item}`));
|
|
4641
|
+
}) }), isDirty && (jsxRuntime.jsx(jsxRuntime.Fragment, { children: dataList.length <= 0 && (jsxRuntime.jsx(jsxRuntime.Fragment, { children: enumPickerLabels?.emptySearchResult ??
|
|
4642
|
+
formI18n.t('empty_search_result') })) }))] })] }) })] })] }));
|
|
3881
4643
|
};
|
|
3882
4644
|
|
|
3883
4645
|
function isEnteringWindow(_ref) {
|
|
@@ -4183,7 +4945,7 @@ function getText(_ref2) {
|
|
|
4183
4945
|
return source.getStringData(textMediaType);
|
|
4184
4946
|
}
|
|
4185
4947
|
|
|
4186
|
-
const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }, placeholder =
|
|
4948
|
+
const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }, placeholder = 'Drop files here or click to upload', }) => {
|
|
4187
4949
|
const ref = React.useRef(null);
|
|
4188
4950
|
const [isDraggedOver, setIsDraggedOver] = React.useState(false);
|
|
4189
4951
|
React.useEffect(() => {
|
|
@@ -4197,7 +4959,7 @@ const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }
|
|
|
4197
4959
|
onDrop: ({ source }) => {
|
|
4198
4960
|
const files = getFiles({ source });
|
|
4199
4961
|
const text = getText({ source });
|
|
4200
|
-
console.log(files, text,
|
|
4962
|
+
console.log(files, text, 'dfposa');
|
|
4201
4963
|
onDrop({ files, text });
|
|
4202
4964
|
},
|
|
4203
4965
|
});
|
|
@@ -4206,9 +4968,9 @@ const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }
|
|
|
4206
4968
|
function getColor(isDraggedOver) {
|
|
4207
4969
|
if (isDraggedOver) {
|
|
4208
4970
|
return {
|
|
4209
|
-
backgroundColor:
|
|
4971
|
+
backgroundColor: 'blue.400',
|
|
4210
4972
|
_dark: {
|
|
4211
|
-
backgroundColor:
|
|
4973
|
+
backgroundColor: 'blue.400',
|
|
4212
4974
|
},
|
|
4213
4975
|
};
|
|
4214
4976
|
}
|
|
@@ -4229,28 +4991,223 @@ const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }
|
|
|
4229
4991
|
const filesArray = [...event.target.files];
|
|
4230
4992
|
onDrop({ files: filesArray });
|
|
4231
4993
|
};
|
|
4232
|
-
return (jsxRuntime.jsxs(react.Grid, { ...getColor(isDraggedOver), ref: ref, cursor:
|
|
4994
|
+
return (jsxRuntime.jsxs(react.Grid, { ...getColor(isDraggedOver), ref: ref, cursor: 'pointer', onClick: handleClick, borderStyle: 'dashed', borderColor: 'colorPalette.400', alignContent: 'center', justifyContent: 'center', borderWidth: 1, borderRadius: 4, minH: "120px", ...gridProps, children: [children, !!children === false && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(react.Flex, { children: placeholder }), jsxRuntime.jsx(react.Input, { type: "file", multiple: true, style: { display: 'none' }, ref: fileInput, onChange: handleChange })] }))] }));
|
|
4233
4995
|
};
|
|
4234
4996
|
|
|
4997
|
+
/**
|
|
4998
|
+
* Format bytes to human-readable string
|
|
4999
|
+
* @param bytes - The number of bytes to format
|
|
5000
|
+
* @returns Formatted string (e.g., "1.5 KB", "2.3 MB")
|
|
5001
|
+
*/
|
|
5002
|
+
function formatBytes(bytes) {
|
|
5003
|
+
if (bytes === 0)
|
|
5004
|
+
return '0 Bytes';
|
|
5005
|
+
const k = 1024;
|
|
5006
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
|
5007
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
5008
|
+
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
|
5009
|
+
}
|
|
5010
|
+
|
|
5011
|
+
function FilePickerDialog({ open, onClose, onSelect, title, filterImageOnly = false, onFetchFiles, labels, translate, colLabel, }) {
|
|
5012
|
+
const [searchTerm, setSearchTerm] = React.useState('');
|
|
5013
|
+
const [selectedFileId, setSelectedFileId] = React.useState('');
|
|
5014
|
+
const [failedImageIds, setFailedImageIds] = React.useState(new Set());
|
|
5015
|
+
const { data: filesData, isLoading, isError, } = reactQuery.useQuery({
|
|
5016
|
+
queryKey: ['file-picker-library', searchTerm],
|
|
5017
|
+
queryFn: async () => {
|
|
5018
|
+
if (!onFetchFiles)
|
|
5019
|
+
return { data: [] };
|
|
5020
|
+
const files = await onFetchFiles(searchTerm.trim() || '');
|
|
5021
|
+
return { data: files };
|
|
5022
|
+
},
|
|
5023
|
+
enabled: open && !!onFetchFiles,
|
|
5024
|
+
});
|
|
5025
|
+
const files = (filesData?.data || []);
|
|
5026
|
+
const filteredFiles = filterImageOnly
|
|
5027
|
+
? files.filter((file) => /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file.name))
|
|
5028
|
+
: files;
|
|
5029
|
+
const handleSelect = () => {
|
|
5030
|
+
if (selectedFileId) {
|
|
5031
|
+
onSelect(selectedFileId);
|
|
5032
|
+
onClose();
|
|
5033
|
+
setSelectedFileId('');
|
|
5034
|
+
setSearchTerm('');
|
|
5035
|
+
}
|
|
5036
|
+
};
|
|
5037
|
+
const handleClose = () => {
|
|
5038
|
+
onClose();
|
|
5039
|
+
setSelectedFileId('');
|
|
5040
|
+
setSearchTerm('');
|
|
5041
|
+
setFailedImageIds(new Set());
|
|
5042
|
+
};
|
|
5043
|
+
const handleImageError = (fileId) => {
|
|
5044
|
+
setFailedImageIds((prev) => new Set(prev).add(fileId));
|
|
5045
|
+
};
|
|
5046
|
+
if (!onFetchFiles)
|
|
5047
|
+
return null;
|
|
5048
|
+
return (jsxRuntime.jsx(DialogRoot, { open: open, onOpenChange: (e) => !e.open && handleClose(), children: jsxRuntime.jsxs(DialogContent, { maxWidth: "800px", maxHeight: "90vh", children: [jsxRuntime.jsxs(DialogHeader, { children: [jsxRuntime.jsx(DialogTitle, { fontSize: "lg", fontWeight: "bold", children: title }), jsxRuntime.jsx(DialogCloseTrigger, {})] }), jsxRuntime.jsx(DialogBody, { children: jsxRuntime.jsxs(react.VStack, { align: "stretch", gap: 4, children: [jsxRuntime.jsxs(react.Box, { position: "relative", children: [jsxRuntime.jsx(react.Input, { placeholder: labels?.searchPlaceholder ??
|
|
5049
|
+
translate(removeIndex(`${colLabel}.search_placeholder`)) ??
|
|
5050
|
+
'Search files...', value: searchTerm, onChange: (e) => setSearchTerm(e.target.value), bg: "bg.panel", border: "1px solid", borderColor: "border.default", colorPalette: "blue", _focus: {
|
|
5051
|
+
borderColor: 'colorPalette.500',
|
|
5052
|
+
_dark: {
|
|
5053
|
+
borderColor: 'colorPalette.400',
|
|
5054
|
+
},
|
|
5055
|
+
boxShadow: {
|
|
5056
|
+
base: '0 0 0 1px var(--chakra-colors-blue-500)',
|
|
5057
|
+
_dark: '0 0 0 1px var(--chakra-colors-blue-400)',
|
|
5058
|
+
},
|
|
5059
|
+
}, pl: 10 }), jsxRuntime.jsx(react.Icon, { as: lu.LuSearch, position: "absolute", left: 3, top: "50%", transform: "translateY(-50%)", color: "fg.muted", boxSize: 4 })] }), isLoading && (jsxRuntime.jsxs(react.Box, { textAlign: "center", py: 8, children: [jsxRuntime.jsx(react.Spinner, { size: "lg", colorPalette: "blue" }), jsxRuntime.jsx(react.Text, { mt: 4, color: "fg.muted", children: labels?.loading ??
|
|
5060
|
+
translate(removeIndex(`${colLabel}.loading`)) ??
|
|
5061
|
+
'Loading files...' })] })), isError && (jsxRuntime.jsx(react.Box, { bg: { base: 'colorPalette.50', _dark: 'colorPalette.900/20' }, border: "1px solid", borderColor: {
|
|
5062
|
+
base: 'colorPalette.200',
|
|
5063
|
+
_dark: 'colorPalette.800',
|
|
5064
|
+
}, colorPalette: "red", borderRadius: "md", p: 4, children: jsxRuntime.jsx(react.Text, { color: {
|
|
5065
|
+
base: 'colorPalette.600',
|
|
5066
|
+
_dark: 'colorPalette.300',
|
|
5067
|
+
}, children: labels?.loadingFailed ??
|
|
5068
|
+
translate(removeIndex(`${colLabel}.error.loading_failed`)) ??
|
|
5069
|
+
'Failed to load files' }) })), !isLoading && !isError && (jsxRuntime.jsx(react.Box, { maxHeight: "400px", overflowY: "auto", children: filteredFiles.length === 0 ? (jsxRuntime.jsx(react.Box, { textAlign: "center", py: 8, children: jsxRuntime.jsx(react.Text, { color: "fg.muted", children: labels?.noFilesFound ??
|
|
5070
|
+
translate(removeIndex(`${colLabel}.no_files_found`)) ??
|
|
5071
|
+
'No files found' }) })) : (jsxRuntime.jsx(react.VStack, { align: "stretch", gap: 2, children: filteredFiles.map((file) => {
|
|
5072
|
+
const isImage = /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file.name);
|
|
5073
|
+
const isSelected = selectedFileId === file.id;
|
|
5074
|
+
const imageFailed = failedImageIds.has(file.id);
|
|
5075
|
+
return (jsxRuntime.jsx(react.Box, { p: 3, border: "2px solid", borderColor: isSelected
|
|
5076
|
+
? {
|
|
5077
|
+
base: 'colorPalette.500',
|
|
5078
|
+
_dark: 'colorPalette.400',
|
|
5079
|
+
}
|
|
5080
|
+
: 'border.default', borderRadius: "md", bg: isSelected
|
|
5081
|
+
? {
|
|
5082
|
+
base: 'colorPalette.50',
|
|
5083
|
+
_dark: 'colorPalette.900/20',
|
|
5084
|
+
}
|
|
5085
|
+
: 'bg.panel', colorPalette: "blue", cursor: "pointer", onClick: () => setSelectedFileId(file.id), _hover: {
|
|
5086
|
+
borderColor: isSelected
|
|
5087
|
+
? {
|
|
5088
|
+
base: 'colorPalette.600',
|
|
5089
|
+
_dark: 'colorPalette.400',
|
|
5090
|
+
}
|
|
5091
|
+
: {
|
|
5092
|
+
base: 'colorPalette.300',
|
|
5093
|
+
_dark: 'colorPalette.400',
|
|
5094
|
+
},
|
|
5095
|
+
bg: isSelected
|
|
5096
|
+
? {
|
|
5097
|
+
base: 'colorPalette.100',
|
|
5098
|
+
_dark: 'colorPalette.800/30',
|
|
5099
|
+
}
|
|
5100
|
+
: 'bg.muted',
|
|
5101
|
+
}, transition: "all 0.2s", children: jsxRuntime.jsxs(react.HStack, { gap: 3, children: [jsxRuntime.jsx(react.Box, { width: "60px", height: "60px", display: "flex", alignItems: "center", justifyContent: "center", bg: "bg.muted", borderRadius: "md", flexShrink: 0, children: isImage && file.url && !imageFailed ? (jsxRuntime.jsx(react.Image, { src: file.url, alt: file.name, boxSize: "60px", objectFit: "cover", borderRadius: "md", onError: () => handleImageError(file.id) })) : isImage && (imageFailed || !file.url) ? (jsxRuntime.jsx(react.Icon, { as: lu.LuImage, boxSize: 6, color: "fg.muted" })) : (jsxRuntime.jsx(react.Icon, { as: lu.LuFile, boxSize: 6, color: "fg.muted" })) }), jsxRuntime.jsxs(react.VStack, { align: "start", flex: 1, gap: 1, children: [jsxRuntime.jsx(react.Text, { fontSize: "sm", fontWeight: "medium", color: "fg.default", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: file.name }), jsxRuntime.jsxs(react.HStack, { gap: 2, children: [file.size && (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "fg.muted", children: typeof file.size === 'number'
|
|
5102
|
+
? formatBytes(file.size)
|
|
5103
|
+
: file.size }) })), file.comment && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [file.size && (jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "fg.muted", children: "\u2022" })), jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "fg.muted", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: file.comment })] }))] })] }), isSelected && (jsxRuntime.jsx(react.Box, { width: "24px", height: "24px", borderRadius: "full", bg: {
|
|
5104
|
+
base: 'colorPalette.500',
|
|
5105
|
+
_dark: 'colorPalette.400',
|
|
5106
|
+
}, colorPalette: "blue", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0, children: jsxRuntime.jsx(react.Text, { color: "white", fontSize: "xs", fontWeight: "bold", children: "\u2713" }) }))] }) }, file.id));
|
|
5107
|
+
}) })) }))] }) }), jsxRuntime.jsx(DialogFooter, { children: jsxRuntime.jsxs(react.HStack, { gap: 3, justify: "end", children: [jsxRuntime.jsx(react.Button, { variant: "outline", onClick: handleClose, borderColor: "border.default", bg: "bg.panel", _hover: { bg: 'bg.muted' }, children: labels?.cancel ??
|
|
5108
|
+
translate(removeIndex(`${colLabel}.cancel`)) ??
|
|
5109
|
+
'Cancel' }), jsxRuntime.jsx(react.Button, { colorPalette: "blue", onClick: handleSelect, disabled: !selectedFileId, children: labels?.select ??
|
|
5110
|
+
translate(removeIndex(`${colLabel}.select`)) ??
|
|
5111
|
+
'Select' })] }) })] }) }));
|
|
5112
|
+
}
|
|
4235
5113
|
const FilePicker = ({ column, schema, prefix }) => {
|
|
4236
5114
|
const { setValue, formState: { errors }, watch, } = reactHookForm.useFormContext();
|
|
4237
|
-
const {
|
|
4238
|
-
const
|
|
5115
|
+
const { filePickerLabels } = useSchemaContext();
|
|
5116
|
+
const formI18n = useFormI18n(column, prefix);
|
|
5117
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1', filePicker, } = schema;
|
|
4239
5118
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4240
|
-
const
|
|
4241
|
-
const
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
5119
|
+
const currentValue = watch(column) ?? [];
|
|
5120
|
+
const currentFiles = Array.isArray(currentValue)
|
|
5121
|
+
? currentValue
|
|
5122
|
+
: [];
|
|
5123
|
+
const colLabel = formI18n.colLabel;
|
|
5124
|
+
const [dialogOpen, setDialogOpen] = React.useState(false);
|
|
5125
|
+
const [failedImageIds, setFailedImageIds] = React.useState(new Set());
|
|
5126
|
+
const { onFetchFiles, enableMediaLibrary = false, filterImageOnly = false, } = filePicker || {};
|
|
5127
|
+
const showMediaLibrary = enableMediaLibrary && !!onFetchFiles;
|
|
5128
|
+
const handleImageError = (fileIdentifier) => {
|
|
5129
|
+
setFailedImageIds((prev) => new Set(prev).add(fileIdentifier));
|
|
5130
|
+
};
|
|
5131
|
+
const handleMediaLibrarySelect = (fileId) => {
|
|
5132
|
+
const newFiles = [...currentFiles, fileId];
|
|
5133
|
+
setValue(colLabel, newFiles);
|
|
5134
|
+
};
|
|
5135
|
+
const handleRemove = (index) => {
|
|
5136
|
+
const newFiles = currentFiles.filter((_, i) => i !== index);
|
|
5137
|
+
setValue(colLabel, newFiles);
|
|
5138
|
+
};
|
|
5139
|
+
const isFileObject = (value) => {
|
|
5140
|
+
return value instanceof File;
|
|
5141
|
+
};
|
|
5142
|
+
const getFileIdentifier = (file, index) => {
|
|
5143
|
+
if (isFileObject(file)) {
|
|
5144
|
+
return `${file.name}-${file.size}-${index}`;
|
|
5145
|
+
}
|
|
5146
|
+
return file;
|
|
5147
|
+
};
|
|
5148
|
+
const getFileName = (file) => {
|
|
5149
|
+
if (isFileObject(file)) {
|
|
5150
|
+
return file.name;
|
|
5151
|
+
}
|
|
5152
|
+
return typeof file === 'string' ? file : 'Unknown file';
|
|
5153
|
+
};
|
|
5154
|
+
const getFileSize = (file) => {
|
|
5155
|
+
if (isFileObject(file)) {
|
|
5156
|
+
return file.size;
|
|
5157
|
+
}
|
|
5158
|
+
return undefined;
|
|
5159
|
+
};
|
|
5160
|
+
const isImageFile = (file) => {
|
|
5161
|
+
if (isFileObject(file)) {
|
|
5162
|
+
return file.type.startsWith('image/');
|
|
5163
|
+
}
|
|
5164
|
+
if (typeof file === 'string') {
|
|
5165
|
+
return /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file);
|
|
5166
|
+
}
|
|
5167
|
+
return false;
|
|
5168
|
+
};
|
|
5169
|
+
const getImageUrl = (file) => {
|
|
5170
|
+
if (isFileObject(file)) {
|
|
5171
|
+
return URL.createObjectURL(file);
|
|
5172
|
+
}
|
|
5173
|
+
return undefined;
|
|
5174
|
+
};
|
|
5175
|
+
return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
5176
|
+
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [jsxRuntime.jsxs(react.VStack, { align: "stretch", gap: 2, children: [jsxRuntime.jsx(FileDropzone, { onDrop: ({ files }) => {
|
|
5177
|
+
const newFiles = files.filter(({ name }) => !currentFiles.some((cur) => {
|
|
5178
|
+
if (isFileObject(cur)) {
|
|
5179
|
+
return cur.name === name;
|
|
5180
|
+
}
|
|
5181
|
+
return false;
|
|
5182
|
+
}));
|
|
5183
|
+
setValue(colLabel, [...currentFiles, ...newFiles]);
|
|
5184
|
+
}, placeholder: filePickerLabels?.fileDropzone ?? formI18n.t('fileDropzone') }), showMediaLibrary && (jsxRuntime.jsx(react.Button, { variant: "outline", onClick: () => setDialogOpen(true), borderColor: "border.default", bg: "bg.panel", _hover: { bg: 'bg.muted' }, children: filePickerLabels?.browseLibrary ??
|
|
5185
|
+
formI18n.t('browse_library') ??
|
|
5186
|
+
'Browse from Library' }))] }), showMediaLibrary && (jsxRuntime.jsx(FilePickerDialog, { open: dialogOpen, onClose: () => setDialogOpen(false), onSelect: handleMediaLibrarySelect, title: filePickerLabels?.dialogTitle ??
|
|
5187
|
+
formI18n.t('dialog_title') ??
|
|
5188
|
+
'Select File', filterImageOnly: filterImageOnly, onFetchFiles: onFetchFiles, labels: filePickerLabels, translate: formI18n.t, colLabel: colLabel })), jsxRuntime.jsx(react.Flex, { flexFlow: 'column', gap: 1, children: currentFiles.map((file, index) => {
|
|
5189
|
+
const fileIdentifier = getFileIdentifier(file, index);
|
|
5190
|
+
const fileName = getFileName(file);
|
|
5191
|
+
const fileSize = getFileSize(file);
|
|
5192
|
+
const isImage = isImageFile(file);
|
|
5193
|
+
const imageUrl = getImageUrl(file);
|
|
5194
|
+
const imageFailed = failedImageIds.has(fileIdentifier);
|
|
5195
|
+
return (jsxRuntime.jsx(react.Card.Root, { variant: 'subtle', colorPalette: "blue", children: jsxRuntime.jsxs(react.Card.Body, { gap: "2", cursor: 'pointer', onClick: () => handleRemove(index), display: 'flex', flexFlow: 'row', alignItems: 'center', padding: '2', border: "2px solid", borderColor: "border.default", borderRadius: "md", _hover: {
|
|
5196
|
+
borderColor: 'colorPalette.300',
|
|
5197
|
+
bg: 'bg.muted',
|
|
5198
|
+
}, transition: "all 0.2s", children: [jsxRuntime.jsx(react.Box, { width: "60px", height: "60px", display: "flex", alignItems: "center", justifyContent: "center", bg: "bg.muted", borderRadius: "md", flexShrink: 0, marginRight: "2", children: isImage && imageUrl && !imageFailed ? (jsxRuntime.jsx(react.Image, { src: imageUrl, alt: fileName, boxSize: "60px", objectFit: "cover", borderRadius: "md", onError: () => handleImageError(fileIdentifier) })) : isImage && (imageFailed || !imageUrl) ? (jsxRuntime.jsx(react.Icon, { as: lu.LuImage, boxSize: 6, color: "fg.muted" })) : (jsxRuntime.jsx(react.Icon, { as: lu.LuFile, boxSize: 6, color: "fg.muted" })) }), jsxRuntime.jsxs(react.VStack, { align: "start", flex: 1, gap: 1, children: [jsxRuntime.jsx(react.Text, { fontSize: "sm", fontWeight: "medium", color: "fg.default", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: fileName }), fileSize !== undefined && (jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "fg.muted", children: formatBytes(fileSize) }))] }), jsxRuntime.jsx(react.Icon, { as: ti.TiDeleteOutline, boxSize: 5, color: "fg.muted" })] }) }, fileIdentifier));
|
|
5199
|
+
}) })] }));
|
|
4252
5200
|
};
|
|
4253
5201
|
|
|
5202
|
+
const ToggleTip = React__namespace.forwardRef(function ToggleTip(props, ref) {
|
|
5203
|
+
const { showArrow, children, portalled = true, content, portalRef, ...rest } = props;
|
|
5204
|
+
return (jsxRuntime.jsxs(react.Popover.Root, { ...rest, positioning: { ...rest.positioning, gutter: 4 }, children: [jsxRuntime.jsx(react.Popover.Trigger, { asChild: true, children: children }), jsxRuntime.jsx(react.Portal, { disabled: !portalled, container: portalRef, children: jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsxs(react.Popover.Content, { width: "auto", px: "2", py: "1", textStyle: "xs", rounded: "sm", ref: ref, children: [showArrow && (jsxRuntime.jsx(react.Popover.Arrow, { children: jsxRuntime.jsx(react.Popover.ArrowTip, {}) })), content] }) }) })] }));
|
|
5205
|
+
});
|
|
5206
|
+
const InfoTip = React__namespace.forwardRef(function InfoTip(props, ref) {
|
|
5207
|
+
const { children, ...rest } = props;
|
|
5208
|
+
return (jsxRuntime.jsx(ToggleTip, { content: children, ...rest, ref: ref, children: jsxRuntime.jsx(react.IconButton, { variant: "ghost", "aria-label": "info", size: "2xs", colorPalette: "colorPalette", children: jsxRuntime.jsx(hi.HiOutlineInformationCircle, {}) }) }));
|
|
5209
|
+
});
|
|
5210
|
+
|
|
4254
5211
|
const getTableData = async ({ serverUrl, in_table, searching = "", where = [], limit = 10, offset = 0, }) => {
|
|
4255
5212
|
if (serverUrl === undefined || serverUrl.length == 0) {
|
|
4256
5213
|
throw new Error("The serverUrl is missing");
|
|
@@ -4281,25 +5238,40 @@ const getTableData = async ({ serverUrl, in_table, searching = "", where = [], l
|
|
|
4281
5238
|
|
|
4282
5239
|
const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
4283
5240
|
const { watch, formState: { errors }, setValue, } = reactHookForm.useFormContext();
|
|
4284
|
-
const { serverUrl, idMap, setIdMap,
|
|
4285
|
-
const
|
|
5241
|
+
const { serverUrl, idMap, setIdMap, schema: parentSchema, idPickerLabels, } = useSchemaContext();
|
|
5242
|
+
const formI18n = useFormI18n(column, prefix);
|
|
5243
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1', renderDisplay, foreign_key, } = schema;
|
|
4286
5244
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4287
|
-
const { table, column: column_ref, display_column, } = foreign_key;
|
|
4288
|
-
const [searchText, setSearchText] = React.useState();
|
|
5245
|
+
const { table, column: column_ref, display_column, customQueryFn, } = foreign_key;
|
|
5246
|
+
const [searchText, setSearchText] = React.useState('');
|
|
4289
5247
|
const [limit, setLimit] = React.useState(10);
|
|
4290
5248
|
const [openSearchResult, setOpenSearchResult] = React.useState();
|
|
4291
5249
|
const [page, setPage] = React.useState(0);
|
|
4292
5250
|
const ref = React.useRef(null);
|
|
4293
|
-
const colLabel =
|
|
5251
|
+
const colLabel = formI18n.colLabel;
|
|
5252
|
+
const watchId = watch(colLabel);
|
|
5253
|
+
const watchIds = isMultiple ? (watch(colLabel) ?? []) : [];
|
|
5254
|
+
// Query for search results
|
|
4294
5255
|
const query = reactQuery.useQuery({
|
|
4295
5256
|
queryKey: [`idpicker`, { column, searchText, limit, page }],
|
|
4296
5257
|
queryFn: async () => {
|
|
5258
|
+
if (customQueryFn) {
|
|
5259
|
+
const { data, idMap } = await customQueryFn({
|
|
5260
|
+
searching: searchText ?? '',
|
|
5261
|
+
limit: limit,
|
|
5262
|
+
offset: page * limit,
|
|
5263
|
+
});
|
|
5264
|
+
setIdMap((state) => {
|
|
5265
|
+
return { ...state, ...idMap };
|
|
5266
|
+
});
|
|
5267
|
+
return data;
|
|
5268
|
+
}
|
|
4297
5269
|
const data = await getTableData({
|
|
4298
5270
|
serverUrl,
|
|
4299
|
-
searching: searchText ??
|
|
5271
|
+
searching: searchText ?? '',
|
|
4300
5272
|
in_table: table,
|
|
4301
5273
|
limit: limit,
|
|
4302
|
-
offset: page *
|
|
5274
|
+
offset: page * limit,
|
|
4303
5275
|
});
|
|
4304
5276
|
const newMap = Object.fromEntries((data ?? { data: [] }).data.map((item) => {
|
|
4305
5277
|
return [
|
|
@@ -4314,27 +5286,38 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
4314
5286
|
});
|
|
4315
5287
|
return data;
|
|
4316
5288
|
},
|
|
4317
|
-
enabled:
|
|
5289
|
+
enabled: openSearchResult === true,
|
|
4318
5290
|
staleTime: 300000,
|
|
4319
5291
|
});
|
|
4320
|
-
|
|
4321
|
-
const
|
|
4322
|
-
const count = data?.count ?? 0;
|
|
4323
|
-
const isDirty = (searchText?.length ?? 0) > 0;
|
|
4324
|
-
const watchId = watch(colLabel);
|
|
4325
|
-
const watchIds = (watch(colLabel) ?? []);
|
|
4326
|
-
reactQuery.useQuery({
|
|
5292
|
+
// Query for currently selected items (to display them properly)
|
|
5293
|
+
const queryDefault = reactQuery.useQuery({
|
|
4327
5294
|
queryKey: [
|
|
4328
|
-
`idpicker`,
|
|
4329
|
-
{ form: parentSchema.title, column,
|
|
5295
|
+
`idpicker-default`,
|
|
5296
|
+
{ form: parentSchema.title, column, id: isMultiple ? watchIds : watchId },
|
|
4330
5297
|
],
|
|
4331
5298
|
queryFn: async () => {
|
|
5299
|
+
if (customQueryFn) {
|
|
5300
|
+
const { data, idMap } = await customQueryFn({
|
|
5301
|
+
searching: watchIds.join(','),
|
|
5302
|
+
limit: isMultiple ? watchIds.length : 1,
|
|
5303
|
+
offset: 0,
|
|
5304
|
+
});
|
|
5305
|
+
setIdMap((state) => {
|
|
5306
|
+
return { ...state, ...idMap };
|
|
5307
|
+
});
|
|
5308
|
+
return data;
|
|
5309
|
+
}
|
|
5310
|
+
if (!watchId && (!watchIds || watchIds.length === 0)) {
|
|
5311
|
+
return { data: [] };
|
|
5312
|
+
}
|
|
5313
|
+
const searchValue = isMultiple ? watchIds.join(',') : watchId;
|
|
4332
5314
|
const data = await getTableData({
|
|
4333
5315
|
serverUrl,
|
|
4334
|
-
searching:
|
|
5316
|
+
searching: searchValue,
|
|
4335
5317
|
in_table: table,
|
|
4336
|
-
|
|
4337
|
-
|
|
5318
|
+
where: [{ id: column_ref, value: isMultiple ? watchIds : watchId }],
|
|
5319
|
+
limit: isMultiple ? watchIds.length : 1,
|
|
5320
|
+
offset: 0,
|
|
4338
5321
|
});
|
|
4339
5322
|
const newMap = Object.fromEntries((data ?? { data: [] }).data.map((item) => {
|
|
4340
5323
|
return [
|
|
@@ -4349,85 +5332,199 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
4349
5332
|
});
|
|
4350
5333
|
return data;
|
|
4351
5334
|
},
|
|
5335
|
+
enabled: isMultiple
|
|
5336
|
+
? Array.isArray(watchIds) && watchIds.length > 0
|
|
5337
|
+
: !!watchId,
|
|
4352
5338
|
});
|
|
5339
|
+
// Effect to load selected values when component mounts
|
|
5340
|
+
React.useEffect(() => {
|
|
5341
|
+
if (isMultiple ? watchIds.length > 0 : !!watchId) {
|
|
5342
|
+
queryDefault.refetch();
|
|
5343
|
+
}
|
|
5344
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
5345
|
+
}, []);
|
|
5346
|
+
// Effect to trigger initial data fetch when popover opens
|
|
5347
|
+
React.useEffect(() => {
|
|
5348
|
+
if (openSearchResult) {
|
|
5349
|
+
// Reset search text when opening the popover
|
|
5350
|
+
setSearchText('');
|
|
5351
|
+
// Reset page to first page
|
|
5352
|
+
setPage(0);
|
|
5353
|
+
// Fetch initial data
|
|
5354
|
+
query.refetch();
|
|
5355
|
+
}
|
|
5356
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
5357
|
+
}, [openSearchResult]);
|
|
4353
5358
|
const onSearchChange = async (event) => {
|
|
4354
5359
|
setSearchText(event.target.value);
|
|
4355
5360
|
setPage(0);
|
|
4356
|
-
|
|
5361
|
+
query.refetch();
|
|
5362
|
+
};
|
|
5363
|
+
const handleLimitChange = (event) => {
|
|
5364
|
+
const newLimit = Number(event.target.value);
|
|
5365
|
+
setLimit(newLimit);
|
|
5366
|
+
// Reset to first page when changing limit
|
|
5367
|
+
setPage(0);
|
|
5368
|
+
// Trigger a new search with the updated limit
|
|
5369
|
+
query.refetch();
|
|
4357
5370
|
};
|
|
5371
|
+
const { isLoading, isFetching, data, isPending, isError } = query;
|
|
5372
|
+
const dataList = data?.data ?? [];
|
|
5373
|
+
const count = data?.count ?? 0;
|
|
4358
5374
|
const getPickedValue = () => {
|
|
4359
5375
|
if (Object.keys(idMap).length <= 0) {
|
|
4360
|
-
return
|
|
5376
|
+
return '';
|
|
4361
5377
|
}
|
|
4362
5378
|
const record = idMap[watchId];
|
|
4363
5379
|
if (record === undefined) {
|
|
4364
|
-
return
|
|
5380
|
+
return '';
|
|
5381
|
+
}
|
|
5382
|
+
if (!!renderDisplay === true) {
|
|
5383
|
+
return renderDisplay(record);
|
|
4365
5384
|
}
|
|
4366
5385
|
return record[display_column];
|
|
4367
5386
|
};
|
|
4368
|
-
return (jsxRuntime.jsxs(Field, { label:
|
|
4369
|
-
gridRow, children: [isMultiple && (jsxRuntime.jsxs(react.Flex, { flexFlow:
|
|
5387
|
+
return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
5388
|
+
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [isMultiple && (jsxRuntime.jsxs(react.Flex, { flexFlow: 'wrap', gap: 1, children: [watchIds.map((id) => {
|
|
4370
5389
|
const item = idMap[id];
|
|
4371
5390
|
if (item === undefined) {
|
|
4372
|
-
return (jsxRuntime.jsx(react.Text, { children:
|
|
5391
|
+
return (jsxRuntime.jsx(react.Text, { children: idPickerLabels?.undefined ?? formI18n.t('undefined') }, id));
|
|
4373
5392
|
}
|
|
4374
5393
|
return (jsxRuntime.jsx(Tag, { closable: true, onClick: () => {
|
|
4375
|
-
setValue(
|
|
5394
|
+
setValue(colLabel, watchIds.filter((itemId) => itemId !== item[column_ref]));
|
|
4376
5395
|
}, children: !!renderDisplay === true
|
|
4377
5396
|
? renderDisplay(item)
|
|
4378
5397
|
: item[display_column] }, id));
|
|
4379
|
-
}), jsxRuntime.jsx(Tag, { cursor:
|
|
5398
|
+
}), jsxRuntime.jsx(Tag, { cursor: 'pointer', onClick: () => {
|
|
4380
5399
|
setOpenSearchResult(true);
|
|
4381
|
-
}, children:
|
|
5400
|
+
}, children: idPickerLabels?.addMore ?? formI18n.t('add_more') })] })), !isMultiple && (jsxRuntime.jsx(Button, { variant: 'outline', onClick: () => {
|
|
4382
5401
|
setOpenSearchResult(true);
|
|
4383
|
-
}, children: getPickedValue() })), jsxRuntime.jsxs(PopoverRoot, { open: openSearchResult, onOpenChange: (e) => setOpenSearchResult(e.open), closeOnInteractOutside: true, initialFocusEl: () => ref.current, positioning: { placement:
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
5402
|
+
}, justifyContent: 'start', children: queryDefault.isLoading ? jsxRuntime.jsx(react.Spinner, { size: "sm" }) : getPickedValue() })), jsxRuntime.jsxs(PopoverRoot, { open: openSearchResult, onOpenChange: (e) => setOpenSearchResult(e.open), closeOnInteractOutside: true, initialFocusEl: () => ref.current, positioning: { placement: 'bottom-start', strategy: 'fixed' }, children: [jsxRuntime.jsx(PopoverTrigger, {}), jsxRuntime.jsx(PopoverContent, { portalled: false, children: jsxRuntime.jsxs(PopoverBody, { display: 'grid', gap: 1, children: [jsxRuntime.jsx(react.Input, { placeholder: idPickerLabels?.typeToSearch ?? formI18n.t('type_to_search'), onChange: onSearchChange, autoComplete: "off", ref: ref, value: searchText }), jsxRuntime.jsx(PopoverTitle, {}), openSearchResult && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [(isFetching || isLoading || isPending) && jsxRuntime.jsx(react.Spinner, {}), isError && (jsxRuntime.jsx(react.Icon, { color: 'red.400', children: jsxRuntime.jsx(bi.BiError, {}) })), jsxRuntime.jsxs(react.Flex, { justifyContent: "space-between", alignItems: "center", children: [jsxRuntime.jsxs(react.Flex, { alignItems: "center", gap: "2", children: [jsxRuntime.jsx(InfoTip, { children: `${idPickerLabels?.total ?? formI18n.t('total')} ${count}, ${idPickerLabels?.showing ?? formI18n.t('showing')} ${limit} ${idPickerLabels?.perPage ?? formI18n.t('per_page', { defaultValue: 'per page' })}` }), jsxRuntime.jsxs(react.Text, { fontSize: "sm", fontWeight: "bold", children: [count, jsxRuntime.jsxs(react.Text, { as: "span", fontSize: "xs", ml: "1", color: "gray.500", children: ["/", ' ', count > 0
|
|
5403
|
+
? `${page * limit + 1}-${Math.min((page + 1) * limit, count)}`
|
|
5404
|
+
: '0'] })] })] }), jsxRuntime.jsx(react.Box, { children: jsxRuntime.jsxs("select", { value: limit, onChange: handleLimitChange, style: {
|
|
5405
|
+
padding: '4px 8px',
|
|
5406
|
+
borderRadius: '4px',
|
|
5407
|
+
border: '1px solid #ccc',
|
|
5408
|
+
fontSize: '14px',
|
|
5409
|
+
}, children: [jsxRuntime.jsx("option", { value: "5", children: "5" }), jsxRuntime.jsx("option", { value: "10", children: "10" }), jsxRuntime.jsx("option", { value: "20", children: "20" }), jsxRuntime.jsx("option", { value: "30", children: "30" })] }) })] }), jsxRuntime.jsx(react.Grid, { overflowY: 'auto', children: dataList.length > 0 ? (jsxRuntime.jsx(react.Flex, { flexFlow: 'column wrap', gap: 1, children: dataList.map((item) => {
|
|
5410
|
+
const selected = isMultiple
|
|
5411
|
+
? watchIds.some((id) => item[column_ref] === id)
|
|
5412
|
+
: watchId === item[column_ref];
|
|
5413
|
+
return (jsxRuntime.jsx(react.Box, { cursor: 'pointer', onClick: () => {
|
|
5414
|
+
if (!isMultiple) {
|
|
5415
|
+
setOpenSearchResult(false);
|
|
5416
|
+
setValue(colLabel, item[column_ref]);
|
|
5417
|
+
return;
|
|
5418
|
+
}
|
|
5419
|
+
// For multiple selection, don't add if already selected
|
|
5420
|
+
if (selected)
|
|
5421
|
+
return;
|
|
5422
|
+
const newSet = new Set([
|
|
5423
|
+
...(watchIds ?? []),
|
|
5424
|
+
item[column_ref],
|
|
5425
|
+
]);
|
|
5426
|
+
setValue(colLabel, [...newSet]);
|
|
5427
|
+
}, opacity: 0.7, _hover: { opacity: 1 }, ...(selected
|
|
5428
|
+
? {
|
|
5429
|
+
color: 'colorPalette.400/50',
|
|
5430
|
+
fontWeight: 'bold',
|
|
5431
|
+
}
|
|
5432
|
+
: {}), children: !!renderDisplay === true
|
|
5433
|
+
? renderDisplay(item)
|
|
5434
|
+
: item[display_column] }, item[column_ref]));
|
|
5435
|
+
}) })) : (jsxRuntime.jsx(react.Text, { children: searchText
|
|
5436
|
+
? idPickerLabels?.emptySearchResult ??
|
|
5437
|
+
formI18n.t('empty_search_result')
|
|
5438
|
+
: idPickerLabels?.initialResults ??
|
|
5439
|
+
formI18n.t('initial_results') })) }), jsxRuntime.jsx(PaginationRoot, { justifySelf: 'center', count: count, pageSize: limit, defaultPage: 1, page: page + 1, onPageChange: (e) => setPage(e.page - 1), children: jsxRuntime.jsxs(react.HStack, { gap: "4", children: [jsxRuntime.jsx(PaginationPrevTrigger, {}), count > 0 && jsxRuntime.jsx(PaginationPageText, {}), jsxRuntime.jsx(PaginationNextTrigger, {})] }) })] }))] }) })] })] }));
|
|
4407
5440
|
};
|
|
4408
5441
|
|
|
4409
5442
|
const NumberInputRoot = React__namespace.forwardRef(function NumberInput(props, ref) {
|
|
4410
5443
|
const { children, ...rest } = props;
|
|
4411
|
-
return (jsxRuntime.
|
|
5444
|
+
return (jsxRuntime.jsx(react.NumberInput.Root, { ref: ref, variant: "outline", ...rest, children: children }));
|
|
4412
5445
|
});
|
|
4413
5446
|
const NumberInputField$1 = react.NumberInput.Input;
|
|
4414
5447
|
react.NumberInput.Scrubber;
|
|
4415
5448
|
react.NumberInput.Label;
|
|
4416
5449
|
|
|
5450
|
+
/**
|
|
5451
|
+
* Gets the error message for a specific field from react-hook-form errors
|
|
5452
|
+
* Prioritizes required errors (#.required) over field-specific validation errors
|
|
5453
|
+
*/
|
|
5454
|
+
const getFieldError = (errors, fieldName) => {
|
|
5455
|
+
// Check for form-level required errors first (highest priority)
|
|
5456
|
+
const requiredError = errors['#.required'];
|
|
5457
|
+
if (requiredError) {
|
|
5458
|
+
const requiredErrorMessage = extractErrorMessage(requiredError);
|
|
5459
|
+
if (requiredErrorMessage) {
|
|
5460
|
+
return requiredErrorMessage;
|
|
5461
|
+
}
|
|
5462
|
+
}
|
|
5463
|
+
// If no required errors, return field-specific error
|
|
5464
|
+
const fieldError = errors[fieldName];
|
|
5465
|
+
if (fieldError) {
|
|
5466
|
+
const fieldErrorMessage = extractErrorMessage(fieldError);
|
|
5467
|
+
if (fieldErrorMessage) {
|
|
5468
|
+
return fieldErrorMessage;
|
|
5469
|
+
}
|
|
5470
|
+
}
|
|
5471
|
+
return undefined;
|
|
5472
|
+
};
|
|
5473
|
+
/**
|
|
5474
|
+
* Helper function to extract error message from various error formats
|
|
5475
|
+
* Only returns message if explicitly provided, no fallback text
|
|
5476
|
+
*/
|
|
5477
|
+
const extractErrorMessage = (error) => {
|
|
5478
|
+
if (!error) {
|
|
5479
|
+
return undefined;
|
|
5480
|
+
}
|
|
5481
|
+
// If it's a simple string error
|
|
5482
|
+
if (typeof error === 'string') {
|
|
5483
|
+
return error;
|
|
5484
|
+
}
|
|
5485
|
+
// If it's an error object with a message property
|
|
5486
|
+
if (error && typeof error === 'object' && 'message' in error) {
|
|
5487
|
+
return error.message;
|
|
5488
|
+
}
|
|
5489
|
+
// If it's an array of errors, get the first one
|
|
5490
|
+
if (Array.isArray(error) && error.length > 0) {
|
|
5491
|
+
const firstError = error[0];
|
|
5492
|
+
if (typeof firstError === 'string') {
|
|
5493
|
+
return firstError;
|
|
5494
|
+
}
|
|
5495
|
+
if (firstError &&
|
|
5496
|
+
typeof firstError === 'object' &&
|
|
5497
|
+
'message' in firstError) {
|
|
5498
|
+
return firstError.message;
|
|
5499
|
+
}
|
|
5500
|
+
}
|
|
5501
|
+
// No fallback - return undefined if no message provided
|
|
5502
|
+
return undefined;
|
|
5503
|
+
};
|
|
5504
|
+
|
|
4417
5505
|
const NumberInputField = ({ schema, column, prefix, }) => {
|
|
4418
5506
|
const { setValue, formState: { errors }, watch, } = reactHookForm.useFormContext();
|
|
4419
5507
|
const { translate } = useSchemaContext();
|
|
4420
|
-
const { required, gridColumn, gridRow } = schema;
|
|
5508
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1', numberStorageType = 'number', } = schema;
|
|
4421
5509
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4422
5510
|
const colLabel = `${prefix}${column}`;
|
|
4423
5511
|
const value = watch(`${colLabel}`);
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
5512
|
+
const fieldError = getFieldError(errors, colLabel);
|
|
5513
|
+
return (jsxRuntime.jsx(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, gridColumn, gridRow, errorText: fieldError
|
|
5514
|
+
? fieldError.includes('required')
|
|
5515
|
+
? translate.t(removeIndex(`${colLabel}.field_required`))
|
|
5516
|
+
: fieldError
|
|
5517
|
+
: undefined, invalid: !!fieldError, children: jsxRuntime.jsx(NumberInputRoot, { value: value, onValueChange: (details) => {
|
|
5518
|
+
// Store as string or number based on configuration, default to number
|
|
5519
|
+
const value = numberStorageType === 'string'
|
|
5520
|
+
? details.value
|
|
5521
|
+
: details.valueAsNumber;
|
|
5522
|
+
setValue(`${colLabel}`, value);
|
|
5523
|
+
}, min: schema.minimum, max: schema.maximum, step: schema.multipleOf || 0.01, allowOverflow: false, clampValueOnBlur: false, inputMode: "decimal", formatOptions: schema.formatOptions, children: jsxRuntime.jsx(NumberInputField$1, { required: isRequired }) }) }));
|
|
4427
5524
|
};
|
|
4428
5525
|
|
|
4429
5526
|
const ObjectInput = ({ schema, column, prefix }) => {
|
|
4430
|
-
const { properties,
|
|
5527
|
+
const { properties, gridColumn = 'span 12', gridRow = 'span 1', required, showLabel = true, } = schema;
|
|
4431
5528
|
const { translate } = useSchemaContext();
|
|
4432
5529
|
const colLabel = `${prefix}${column}`;
|
|
4433
5530
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
@@ -4435,26 +5532,32 @@ const ObjectInput = ({ schema, column, prefix }) => {
|
|
|
4435
5532
|
if (properties === undefined) {
|
|
4436
5533
|
throw new Error(`properties is undefined when using ObjectInput`);
|
|
4437
5534
|
}
|
|
4438
|
-
return (jsxRuntime.jsxs(react.Box, { gridRow, gridColumn, children: [jsxRuntime.jsxs(react.Box, { as: "label",
|
|
5535
|
+
return (jsxRuntime.jsxs(react.Box, { gridRow, gridColumn, children: [showLabel && (jsxRuntime.jsxs(react.Box, { as: "label", children: [`${translate.t(removeIndex(`${colLabel}.field_label`))}`, isRequired && jsxRuntime.jsx("span", { children: "*" })] })), jsxRuntime.jsx(react.Grid, { bgColor: { base: 'colorPalette.100', _dark: 'colorPalette.900' }, p: 2, borderRadius: 4, borderWidth: 1, borderColor: {
|
|
5536
|
+
base: 'colorPalette.200',
|
|
5537
|
+
_dark: 'colorPalette.800',
|
|
5538
|
+
}, gap: "4", padding: '4', gridTemplateColumns: 'repeat(12, 1fr)', autoFlow: 'row', children: Object.keys(properties ?? {}).map((key) => {
|
|
4439
5539
|
return (
|
|
4440
5540
|
// @ts-expect-error find suitable types
|
|
4441
5541
|
jsxRuntime.jsx(ColumnRenderer, { column: `${key}`,
|
|
4442
5542
|
prefix: `${prefix}${column}.`,
|
|
4443
|
-
properties
|
|
4444
|
-
|
|
5543
|
+
properties,
|
|
5544
|
+
parentRequired: required }, `form-${colLabel}-${key}`));
|
|
5545
|
+
}) }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: 'red.400', children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
|
|
4445
5546
|
};
|
|
4446
5547
|
|
|
4447
5548
|
const RecordInput$1 = ({ column, schema, prefix }) => {
|
|
4448
5549
|
const { formState: { errors }, setValue, getValues, } = reactHookForm.useFormContext();
|
|
4449
5550
|
const { translate } = useSchemaContext();
|
|
4450
|
-
const { required, gridColumn, gridRow } = schema;
|
|
5551
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1' } = schema;
|
|
4451
5552
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4452
5553
|
const entries = Object.entries(getValues(column) ?? {});
|
|
4453
5554
|
const [showNewEntries, setShowNewEntries] = React.useState(false);
|
|
4454
5555
|
const [newKey, setNewKey] = React.useState();
|
|
4455
5556
|
const [newValue, setNewValue] = React.useState();
|
|
4456
|
-
return (jsxRuntime.jsxs(Field, { label: `${translate.t(`${column}.
|
|
4457
|
-
|
|
5557
|
+
return (jsxRuntime.jsxs(Field, { label: `${translate.t(`${column}.field_label`)}`, required: isRequired, alignItems: 'stretch', gridColumn, gridRow, errorText: errors[`${column}`]
|
|
5558
|
+
? translate.t(`${column}.field_required`)
|
|
5559
|
+
: undefined, invalid: !!errors[column], children: [entries.map(([key, value]) => {
|
|
5560
|
+
return (jsxRuntime.jsxs(react.Grid, { templateColumns: '1fr 1fr auto', gap: 1, children: [jsxRuntime.jsx(react.Input, { value: key, onChange: (e) => {
|
|
4458
5561
|
const filtered = entries.filter(([target]) => {
|
|
4459
5562
|
return target !== key;
|
|
4460
5563
|
});
|
|
@@ -4464,17 +5567,17 @@ const RecordInput$1 = ({ column, schema, prefix }) => {
|
|
|
4464
5567
|
...getValues(column),
|
|
4465
5568
|
[key]: e.target.value,
|
|
4466
5569
|
});
|
|
4467
|
-
}, autoComplete: "off" }), jsxRuntime.jsx(react.IconButton, { variant:
|
|
5570
|
+
}, autoComplete: "off" }), jsxRuntime.jsx(react.IconButton, { variant: 'ghost', onClick: () => {
|
|
4468
5571
|
const filtered = entries.filter(([target]) => {
|
|
4469
5572
|
return target !== key;
|
|
4470
5573
|
});
|
|
4471
5574
|
setValue(column, Object.fromEntries([...filtered]));
|
|
4472
5575
|
}, children: jsxRuntime.jsx(cg.CgClose, {}) })] }));
|
|
4473
|
-
}), jsxRuntime.jsx(react.Show, { when: showNewEntries, children: jsxRuntime.jsxs(react.Card.Root, { children: [jsxRuntime.jsx(react.Card.Body, { gap: "2", children: jsxRuntime.jsxs(react.Grid, { templateColumns:
|
|
5576
|
+
}), jsxRuntime.jsx(react.Show, { when: showNewEntries, children: jsxRuntime.jsxs(react.Card.Root, { children: [jsxRuntime.jsx(react.Card.Body, { gap: "2", children: jsxRuntime.jsxs(react.Grid, { templateColumns: '1fr 1fr auto', gap: 1, children: [jsxRuntime.jsx(react.Input, { value: newKey, onChange: (e) => {
|
|
4474
5577
|
setNewKey(e.target.value);
|
|
4475
5578
|
}, autoComplete: "off" }), jsxRuntime.jsx(react.Input, { value: newValue, onChange: (e) => {
|
|
4476
5579
|
setNewValue(e.target.value);
|
|
4477
|
-
}, autoComplete: "off" })] }) }), jsxRuntime.jsxs(react.Card.Footer, { justifyContent: "flex-end", children: [jsxRuntime.jsx(react.IconButton, { variant:
|
|
5580
|
+
}, autoComplete: "off" })] }) }), jsxRuntime.jsxs(react.Card.Footer, { justifyContent: "flex-end", children: [jsxRuntime.jsx(react.IconButton, { variant: 'subtle', onClick: () => {
|
|
4478
5581
|
setShowNewEntries(false);
|
|
4479
5582
|
setNewKey(undefined);
|
|
4480
5583
|
setNewValue(undefined);
|
|
@@ -4493,16 +5596,17 @@ const RecordInput$1 = ({ column, schema, prefix }) => {
|
|
|
4493
5596
|
setShowNewEntries(true);
|
|
4494
5597
|
setNewKey(undefined);
|
|
4495
5598
|
setNewValue(undefined);
|
|
4496
|
-
}, children: translate.t(`${column}.addNew`) })
|
|
5599
|
+
}, children: translate.t(`${column}.addNew`) })] }));
|
|
4497
5600
|
};
|
|
4498
5601
|
|
|
4499
5602
|
const StringInputField = ({ column, schema, prefix, }) => {
|
|
4500
5603
|
const { register, formState: { errors }, } = reactHookForm.useFormContext();
|
|
4501
5604
|
const { translate } = useSchemaContext();
|
|
4502
|
-
const { required, gridColumn, gridRow } = schema;
|
|
5605
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1' } = schema;
|
|
4503
5606
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4504
5607
|
const colLabel = `${prefix}${column}`;
|
|
4505
|
-
|
|
5608
|
+
const fieldError = getFieldError(errors, colLabel);
|
|
5609
|
+
return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsx(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, gridColumn: gridColumn, gridRow: gridRow, errorText: fieldError, invalid: !!fieldError, children: jsxRuntime.jsx(react.Input, { ...register(`${colLabel}`, { required: isRequired }), autoComplete: "off" }) }) }));
|
|
4506
5610
|
};
|
|
4507
5611
|
|
|
4508
5612
|
const RadioCardItem = React__namespace.forwardRef(function RadioCardItem(props, ref) {
|
|
@@ -4600,145 +5704,796 @@ const TagPicker = ({ column, schema, prefix }) => {
|
|
|
4600
5704
|
}), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: (errors[`${column}`]?.message ?? "No error message") }))] }));
|
|
4601
5705
|
};
|
|
4602
5706
|
|
|
5707
|
+
const Textarea = React.forwardRef(({ value, defaultValue, placeholder, onChange, onFocus, onBlur, disabled = false, readOnly = false, className, rows = 4, maxLength, autoFocus = false, invalid = false, required = false, label, helperText, errorText, ...props }, ref) => {
|
|
5708
|
+
const contentEditableRef = React.useRef(null);
|
|
5709
|
+
const isControlled = value !== undefined;
|
|
5710
|
+
// Handle input changes
|
|
5711
|
+
const handleInput = (e) => {
|
|
5712
|
+
const text = e.currentTarget.textContent || "";
|
|
5713
|
+
// Check maxLength if specified
|
|
5714
|
+
if (maxLength && text.length > maxLength) {
|
|
5715
|
+
e.currentTarget.textContent = text.slice(0, maxLength);
|
|
5716
|
+
// Move cursor to end
|
|
5717
|
+
const selection = window.getSelection();
|
|
5718
|
+
if (selection) {
|
|
5719
|
+
selection.selectAllChildren(e.currentTarget);
|
|
5720
|
+
selection.collapseToEnd();
|
|
5721
|
+
}
|
|
5722
|
+
return;
|
|
5723
|
+
}
|
|
5724
|
+
onChange?.(text);
|
|
5725
|
+
};
|
|
5726
|
+
// Handle paste events to strip formatting and respect maxLength
|
|
5727
|
+
const handlePaste = (e) => {
|
|
5728
|
+
e.preventDefault();
|
|
5729
|
+
const text = e.clipboardData.getData('text/plain');
|
|
5730
|
+
const currentText = e.currentTarget.textContent || "";
|
|
5731
|
+
let pasteText = text;
|
|
5732
|
+
if (maxLength) {
|
|
5733
|
+
const remainingLength = maxLength - currentText.length;
|
|
5734
|
+
pasteText = text.slice(0, remainingLength);
|
|
5735
|
+
}
|
|
5736
|
+
document.execCommand('insertText', false, pasteText);
|
|
5737
|
+
};
|
|
5738
|
+
// Set initial content
|
|
5739
|
+
React.useEffect(() => {
|
|
5740
|
+
if (contentEditableRef.current && !isControlled) {
|
|
5741
|
+
const initialValue = defaultValue || "";
|
|
5742
|
+
if (contentEditableRef.current.textContent !== initialValue) {
|
|
5743
|
+
contentEditableRef.current.textContent = initialValue;
|
|
5744
|
+
}
|
|
5745
|
+
}
|
|
5746
|
+
}, [defaultValue, isControlled]);
|
|
5747
|
+
// Update content when value changes (controlled mode)
|
|
5748
|
+
React.useEffect(() => {
|
|
5749
|
+
if (contentEditableRef.current && isControlled && value !== undefined) {
|
|
5750
|
+
if (contentEditableRef.current.textContent !== value) {
|
|
5751
|
+
contentEditableRef.current.textContent = value;
|
|
5752
|
+
}
|
|
5753
|
+
}
|
|
5754
|
+
}, [value, isControlled]);
|
|
5755
|
+
// Auto focus
|
|
5756
|
+
React.useEffect(() => {
|
|
5757
|
+
if (autoFocus && contentEditableRef.current) {
|
|
5758
|
+
contentEditableRef.current.focus();
|
|
5759
|
+
}
|
|
5760
|
+
}, [autoFocus]);
|
|
5761
|
+
// Forward ref
|
|
5762
|
+
React.useEffect(() => {
|
|
5763
|
+
if (typeof ref === 'function') {
|
|
5764
|
+
ref(contentEditableRef.current);
|
|
5765
|
+
}
|
|
5766
|
+
else if (ref) {
|
|
5767
|
+
ref.current = contentEditableRef.current;
|
|
5768
|
+
}
|
|
5769
|
+
}, [ref]);
|
|
5770
|
+
const textareaElement = (jsxRuntime.jsx(react.Box, { ref: contentEditableRef, contentEditable: !disabled && !readOnly, onInput: handleInput, onPaste: handlePaste, onFocus: onFocus, onBlur: onBlur, className: className, minHeight: `${rows * 1.5}em`, padding: "2", border: "1px solid", borderColor: invalid ? "red.500" : "gray.200", borderRadius: "md", outline: "none", _focus: {
|
|
5771
|
+
borderColor: invalid ? "red.500" : "blue.500",
|
|
5772
|
+
boxShadow: `0 0 0 1px ${invalid ? "red.500" : "blue.500"}`,
|
|
5773
|
+
}, _disabled: {
|
|
5774
|
+
opacity: 0.6,
|
|
5775
|
+
cursor: "not-allowed",
|
|
5776
|
+
bg: "gray.50",
|
|
5777
|
+
}, _empty: {
|
|
5778
|
+
_before: {
|
|
5779
|
+
content: placeholder ? `"${placeholder}"` : undefined,
|
|
5780
|
+
color: "gray.400",
|
|
5781
|
+
pointerEvents: "none",
|
|
5782
|
+
}
|
|
5783
|
+
}, whiteSpace: "pre-wrap", overflowWrap: "break-word", overflow: "auto", maxHeight: `${rows * 4}em`, suppressContentEditableWarning: true, ...props }));
|
|
5784
|
+
// If we have additional field props, wrap in Field component
|
|
5785
|
+
if (label || helperText || errorText || required) {
|
|
5786
|
+
return (jsxRuntime.jsxs(react.Field.Root, { invalid: invalid, required: required, children: [label && (jsxRuntime.jsxs(react.Field.Label, { children: [label, required && jsxRuntime.jsx(react.Field.RequiredIndicator, {})] })), textareaElement, helperText && jsxRuntime.jsx(react.Field.HelperText, { children: helperText }), errorText && jsxRuntime.jsx(react.Field.ErrorText, { children: errorText })] }));
|
|
5787
|
+
}
|
|
5788
|
+
return textareaElement;
|
|
5789
|
+
});
|
|
5790
|
+
Textarea.displayName = "Textarea";
|
|
5791
|
+
|
|
5792
|
+
const TextAreaInput = ({ column, schema, prefix, }) => {
|
|
5793
|
+
const { register, formState: { errors }, } = reactHookForm.useFormContext();
|
|
5794
|
+
const { translate } = useSchemaContext();
|
|
5795
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1' } = schema;
|
|
5796
|
+
const isRequired = required?.some((columnId) => columnId === column);
|
|
5797
|
+
const colLabel = `${prefix}${column}`;
|
|
5798
|
+
const form = reactHookForm.useFormContext();
|
|
5799
|
+
const { setValue, watch } = form;
|
|
5800
|
+
const fieldError = getFieldError(errors, colLabel);
|
|
5801
|
+
const watchValue = watch(colLabel);
|
|
5802
|
+
return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsx(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, gridColumn: gridColumn ?? 'span 4', gridRow: gridRow ?? 'span 1', display: "grid", errorText: fieldError
|
|
5803
|
+
? fieldError.includes('required')
|
|
5804
|
+
? translate.t(removeIndex(`${colLabel}.field_required`))
|
|
5805
|
+
: fieldError
|
|
5806
|
+
: undefined, invalid: !!fieldError, children: jsxRuntime.jsx(Textarea, { value: watchValue, onChange: (value) => setValue(colLabel, value) }) }) }));
|
|
5807
|
+
};
|
|
5808
|
+
|
|
5809
|
+
function TimePicker$1({ hour, setHour, minute, setMinute, meridiem, setMeridiem, meridiemLabel = {
|
|
5810
|
+
am: "am",
|
|
5811
|
+
pm: "pm",
|
|
5812
|
+
}, onChange = (_newValue) => { }, timezone = "Asia/Hong_Kong", }) {
|
|
5813
|
+
const handleClear = () => {
|
|
5814
|
+
setHour(null);
|
|
5815
|
+
setMinute(null);
|
|
5816
|
+
setMeridiem(null);
|
|
5817
|
+
setInputValue("");
|
|
5818
|
+
setShowInput(false);
|
|
5819
|
+
onChange({ hour: null, minute: null, meridiem: null });
|
|
5820
|
+
};
|
|
5821
|
+
const getTimeString = (hour, minute, meridiem) => {
|
|
5822
|
+
if (hour === null || minute === null || meridiem === null) {
|
|
5823
|
+
return "";
|
|
5824
|
+
}
|
|
5825
|
+
// if the hour is 24, set the hour to 0
|
|
5826
|
+
if (hour === 24) {
|
|
5827
|
+
return dayjs().tz(timezone).hour(0).minute(minute).format("HH:mmZ");
|
|
5828
|
+
}
|
|
5829
|
+
// use dayjs to format the time at current timezone
|
|
5830
|
+
// if meridiem is pm, add 12 hours
|
|
5831
|
+
let newHour = hour;
|
|
5832
|
+
if (meridiem === "pm" && hour !== 12) {
|
|
5833
|
+
newHour = hour + 12;
|
|
5834
|
+
}
|
|
5835
|
+
// if the hour is 12, set the meridiem to am, and set the hour to 0
|
|
5836
|
+
else if (meridiem === "am" && hour === 12) {
|
|
5837
|
+
newHour = 0;
|
|
5838
|
+
}
|
|
5839
|
+
return dayjs().tz(timezone).hour(newHour).minute(minute).format("HH:mmZ");
|
|
5840
|
+
};
|
|
5841
|
+
const stringTime = getTimeString(hour, minute, meridiem);
|
|
5842
|
+
const [inputValue, setInputValue] = React.useState("");
|
|
5843
|
+
const [showInput, setShowInput] = React.useState(false);
|
|
5844
|
+
const handleBlur = (text) => {
|
|
5845
|
+
// ignore all non-numeric characters
|
|
5846
|
+
if (!text) {
|
|
5847
|
+
return;
|
|
5848
|
+
}
|
|
5849
|
+
const value = text.replace(/[^0-9apm]/g, "");
|
|
5850
|
+
if (value === "") {
|
|
5851
|
+
handleClear();
|
|
5852
|
+
return;
|
|
5853
|
+
}
|
|
5854
|
+
// if the value is a valid time, parse it and set the hour, minute, and meridiem
|
|
5855
|
+
// if the value is not a valid time, set the stringTime to the value
|
|
5856
|
+
// first two characters are the hour
|
|
5857
|
+
// next two characters are the minute
|
|
5858
|
+
// final two characters are the meridiem
|
|
5859
|
+
const hour = parseInt(value.slice(0, 2));
|
|
5860
|
+
const minute = parseInt(value.slice(2, 4));
|
|
5861
|
+
const meridiem = value.slice(4, 6);
|
|
5862
|
+
// validate the hour and minute
|
|
5863
|
+
if (isNaN(hour) || isNaN(minute)) {
|
|
5864
|
+
setInputValue("");
|
|
5865
|
+
return;
|
|
5866
|
+
}
|
|
5867
|
+
// if the hour is larger than 24, set the hour to 24
|
|
5868
|
+
if (hour > 24) {
|
|
5869
|
+
setInputValue("");
|
|
5870
|
+
return;
|
|
5871
|
+
}
|
|
5872
|
+
let newHour = hour;
|
|
5873
|
+
let newMinute = minute;
|
|
5874
|
+
let newMeridiem = meridiem;
|
|
5875
|
+
// if the hour is 24, set the meridiem to am, and set the hour to 0
|
|
5876
|
+
if (hour === 24) {
|
|
5877
|
+
newMeridiem = "am";
|
|
5878
|
+
newHour = 0;
|
|
5879
|
+
}
|
|
5880
|
+
// if the hour is greater than 12, set the meridiem to pm, and subtract 12 from the hour
|
|
5881
|
+
else if (hour > 12) {
|
|
5882
|
+
newMeridiem = "pm";
|
|
5883
|
+
newHour = hour - 12;
|
|
5884
|
+
}
|
|
5885
|
+
// if the hour is 12, set the meridiem to pm, and set the hour to 12
|
|
5886
|
+
else if (hour === 12) {
|
|
5887
|
+
newMeridiem = "pm";
|
|
5888
|
+
newHour = 12;
|
|
5889
|
+
}
|
|
5890
|
+
// if the hour is 0, set the meridiem to am, and set the hour to 12
|
|
5891
|
+
else if (hour === 0) {
|
|
5892
|
+
newMeridiem = "am";
|
|
5893
|
+
newHour = 12;
|
|
5894
|
+
}
|
|
5895
|
+
else {
|
|
5896
|
+
newMeridiem = meridiem ?? "am";
|
|
5897
|
+
newHour = hour;
|
|
5898
|
+
}
|
|
5899
|
+
if (minute > 59) {
|
|
5900
|
+
newMinute = 0;
|
|
5901
|
+
}
|
|
5902
|
+
else {
|
|
5903
|
+
newMinute = minute;
|
|
5904
|
+
}
|
|
5905
|
+
onChange({
|
|
5906
|
+
hour: newHour,
|
|
5907
|
+
minute: newMinute,
|
|
5908
|
+
meridiem: newMeridiem,
|
|
5909
|
+
});
|
|
5910
|
+
setShowInput(false);
|
|
5911
|
+
};
|
|
5912
|
+
const handleKeyDown = (e) => {
|
|
5913
|
+
if (e.key === "Enter") {
|
|
5914
|
+
handleBlur(e.currentTarget.value);
|
|
5915
|
+
}
|
|
5916
|
+
};
|
|
5917
|
+
const inputRef = React.useRef(null);
|
|
5918
|
+
return (jsxRuntime.jsxs(react.Grid, { justifyContent: "center", alignItems: "center", templateColumns: "200px auto", gap: "2", width: "auto", minWidth: "250px", children: [jsxRuntime.jsx(react.Input, { onKeyDown: handleKeyDown, onChange: (e) => {
|
|
5919
|
+
setInputValue(e.currentTarget.value);
|
|
5920
|
+
}, onBlur: (e) => {
|
|
5921
|
+
handleBlur(e.currentTarget.value);
|
|
5922
|
+
}, onFocus: (e) => {
|
|
5923
|
+
e.currentTarget.select();
|
|
5924
|
+
}, value: inputValue, display: showInput ? undefined : "none", ref: inputRef }), jsxRuntime.jsxs(react.Button, { onClick: () => {
|
|
5925
|
+
setShowInput(true);
|
|
5926
|
+
setInputValue(dayjs(`1970-01-01T${getTimeString(hour, minute, meridiem)}`, "hh:mmZ").format("HH:mm"));
|
|
5927
|
+
inputRef.current?.focus();
|
|
5928
|
+
}, display: showInput ? "none" : "flex", alignItems: "center", justifyContent: "start", variant: "outline", gap: 2, children: [jsxRuntime.jsx(react.Icon, { size: "sm", children: jsxRuntime.jsx(bs.BsClock, {}) }), jsxRuntime.jsx(react.Text, { fontSize: "sm", children: stringTime
|
|
5929
|
+
? dayjs(`1970-01-01T${stringTime}`, "hh:mmZ").format("hh:mm a")
|
|
5930
|
+
: "" })] }), jsxRuntime.jsx(react.Button, { onClick: handleClear, size: "sm", variant: "ghost", children: jsxRuntime.jsx(md.MdCancel, {}) })] }));
|
|
5931
|
+
}
|
|
5932
|
+
|
|
5933
|
+
dayjs.extend(timezone);
|
|
5934
|
+
const TimePicker = ({ column, schema, prefix }) => {
|
|
5935
|
+
const { watch, formState: { errors }, setValue, } = reactHookForm.useFormContext();
|
|
5936
|
+
const { translate, timezone } = useSchemaContext();
|
|
5937
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1', timeFormat = 'HH:mm:ssZ', displayTimeFormat = 'hh:mm A', } = schema;
|
|
5938
|
+
const isRequired = required?.some((columnId) => columnId === column);
|
|
5939
|
+
const colLabel = `${prefix}${column}`;
|
|
5940
|
+
const [open, setOpen] = React.useState(false);
|
|
5941
|
+
const value = watch(colLabel);
|
|
5942
|
+
const displayedTime = dayjs(`1970-01-01T${value}`).tz(timezone).isValid()
|
|
5943
|
+
? dayjs(`1970-01-01T${value}`).tz(timezone).format(displayTimeFormat)
|
|
5944
|
+
: '';
|
|
5945
|
+
// Parse the initial time parts from the time string (HH:mm:ssZ)
|
|
5946
|
+
const parseTime = (time) => {
|
|
5947
|
+
if (!time)
|
|
5948
|
+
return { hour: 12, minute: 0, meridiem: 'am' };
|
|
5949
|
+
const parsed = dayjs(`1970-01-01T${time}`).tz(timezone);
|
|
5950
|
+
if (!parsed.isValid()) {
|
|
5951
|
+
return { hour: 12, minute: 0, meridiem: 'am' };
|
|
5952
|
+
}
|
|
5953
|
+
let hour = parsed.hour();
|
|
5954
|
+
const minute = parsed.minute();
|
|
5955
|
+
const meridiem = hour >= 12 ? 'pm' : 'am';
|
|
5956
|
+
if (hour === 0)
|
|
5957
|
+
hour = 12;
|
|
5958
|
+
else if (hour > 12)
|
|
5959
|
+
hour -= 12;
|
|
5960
|
+
return { hour, minute, meridiem };
|
|
5961
|
+
};
|
|
5962
|
+
const initialTime = parseTime(value);
|
|
5963
|
+
const [hour, setHour] = React.useState(initialTime.hour);
|
|
5964
|
+
const [minute, setMinute] = React.useState(initialTime.minute);
|
|
5965
|
+
const [meridiem, setMeridiem] = React.useState(initialTime.meridiem);
|
|
5966
|
+
React.useEffect(() => {
|
|
5967
|
+
const { hour, minute, meridiem } = parseTime(value);
|
|
5968
|
+
setHour(hour);
|
|
5969
|
+
setMinute(minute);
|
|
5970
|
+
setMeridiem(meridiem);
|
|
5971
|
+
}, [value]);
|
|
5972
|
+
const getTimeString = (hour, minute, meridiem) => {
|
|
5973
|
+
if (hour === null || minute === null || meridiem === null)
|
|
5974
|
+
return null;
|
|
5975
|
+
let newHour = hour;
|
|
5976
|
+
if (meridiem === 'pm' && hour !== 12) {
|
|
5977
|
+
newHour = hour + 12;
|
|
5978
|
+
}
|
|
5979
|
+
return dayjs()
|
|
5980
|
+
.tz(timezone)
|
|
5981
|
+
.hour(newHour)
|
|
5982
|
+
.minute(minute)
|
|
5983
|
+
.second(0)
|
|
5984
|
+
.format(timeFormat);
|
|
5985
|
+
};
|
|
5986
|
+
// Handle changes to time parts
|
|
5987
|
+
const handleTimeChange = ({ hour: newHour, minute: newMinute, meridiem: newMeridiem, }) => {
|
|
5988
|
+
setHour(newHour);
|
|
5989
|
+
setMinute(newMinute);
|
|
5990
|
+
setMeridiem(newMeridiem);
|
|
5991
|
+
const timeString = getTimeString(newHour, newMinute, newMeridiem);
|
|
5992
|
+
setValue(colLabel, timeString, { shouldValidate: true, shouldDirty: true });
|
|
5993
|
+
};
|
|
5994
|
+
return (jsxRuntime.jsx(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, alignItems: 'stretch', gridColumn,
|
|
5995
|
+
gridRow, errorText: errors[`${colLabel}`]
|
|
5996
|
+
? translate.t(removeIndex(`${colLabel}.field_required`))
|
|
5997
|
+
: undefined, invalid: !!errors[colLabel], children: jsxRuntime.jsxs(react.Popover.Root, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, children: [jsxRuntime.jsx(react.Popover.Trigger, { asChild: true, children: jsxRuntime.jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
|
|
5998
|
+
setOpen(true);
|
|
5999
|
+
}, justifyContent: 'start', children: [jsxRuntime.jsx(io.IoMdClock, {}), !!value ? `${displayedTime}` : ''] }) }), jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { children: jsxRuntime.jsx(react.Popover.Body, { children: jsxRuntime.jsx(TimePicker$1, { hour: hour, setHour: setHour, minute: minute, setMinute: setMinute, meridiem: meridiem, setMeridiem: setMeridiem, onChange: handleTimeChange, meridiemLabel: {
|
|
6000
|
+
am: translate.t(`common.am`, { defaultValue: 'AM' }),
|
|
6001
|
+
pm: translate.t(`common.pm`, { defaultValue: 'PM' }),
|
|
6002
|
+
} }) }) }) })] }) }));
|
|
6003
|
+
};
|
|
6004
|
+
|
|
6005
|
+
function IsoTimePicker({ hour, setHour, minute, setMinute, second, setSecond, onChange = (_newValue) => { }, }) {
|
|
6006
|
+
// Refs for focus management
|
|
6007
|
+
const hourInputRef = React.useRef(null);
|
|
6008
|
+
const minuteInputRef = React.useRef(null);
|
|
6009
|
+
const secondInputRef = React.useRef(null);
|
|
6010
|
+
// Centralized handler for key events, value changes, and focus management
|
|
6011
|
+
const handleKeyDown = (e, field) => {
|
|
6012
|
+
const input = e.target;
|
|
6013
|
+
const value = input.value;
|
|
6014
|
+
// Handle navigation between fields
|
|
6015
|
+
if (e.key === "Tab") {
|
|
6016
|
+
return;
|
|
6017
|
+
}
|
|
6018
|
+
if (e.key === ":" && field === "hour") {
|
|
6019
|
+
e.preventDefault();
|
|
6020
|
+
minuteInputRef.current?.focus();
|
|
6021
|
+
return;
|
|
6022
|
+
}
|
|
6023
|
+
if (e.key === ":" && field === "minute") {
|
|
6024
|
+
e.preventDefault();
|
|
6025
|
+
secondInputRef.current?.focus();
|
|
6026
|
+
return;
|
|
6027
|
+
}
|
|
6028
|
+
if (e.key === "Backspace" && value === "") {
|
|
6029
|
+
e.preventDefault();
|
|
6030
|
+
if (field === "minute") {
|
|
6031
|
+
hourInputRef.current?.focus();
|
|
6032
|
+
}
|
|
6033
|
+
else if (field === "second") {
|
|
6034
|
+
minuteInputRef.current?.focus();
|
|
6035
|
+
}
|
|
6036
|
+
return;
|
|
6037
|
+
}
|
|
6038
|
+
// Handle number inputs
|
|
6039
|
+
if (field === "hour") {
|
|
6040
|
+
if (e.key.match(/^[0-9]$/)) {
|
|
6041
|
+
const newValue = value + e.key;
|
|
6042
|
+
const numValue = parseInt(newValue, 10);
|
|
6043
|
+
if (numValue > 23) {
|
|
6044
|
+
const digitValue = parseInt(e.key, 10);
|
|
6045
|
+
setHour(digitValue);
|
|
6046
|
+
onChange({ hour: digitValue, minute, second });
|
|
6047
|
+
return;
|
|
6048
|
+
}
|
|
6049
|
+
if (numValue >= 0 && numValue <= 23) {
|
|
6050
|
+
setHour(numValue);
|
|
6051
|
+
onChange({ hour: numValue, minute, second });
|
|
6052
|
+
e.preventDefault();
|
|
6053
|
+
minuteInputRef.current?.focus();
|
|
6054
|
+
}
|
|
6055
|
+
}
|
|
6056
|
+
}
|
|
6057
|
+
else if (field === "minute") {
|
|
6058
|
+
if (e.key.match(/^[0-9]$/)) {
|
|
6059
|
+
const newValue = value + e.key;
|
|
6060
|
+
const numValue = parseInt(newValue, 10);
|
|
6061
|
+
if (numValue > 59) {
|
|
6062
|
+
const digitValue = parseInt(e.key, 10);
|
|
6063
|
+
setMinute(digitValue);
|
|
6064
|
+
onChange({ hour, minute: digitValue, second });
|
|
6065
|
+
return;
|
|
6066
|
+
}
|
|
6067
|
+
if (numValue >= 0 && numValue <= 59) {
|
|
6068
|
+
setMinute(numValue);
|
|
6069
|
+
onChange({ hour, minute: numValue, second });
|
|
6070
|
+
e.preventDefault();
|
|
6071
|
+
secondInputRef.current?.focus();
|
|
6072
|
+
}
|
|
6073
|
+
}
|
|
6074
|
+
}
|
|
6075
|
+
else if (field === "second") {
|
|
6076
|
+
if (e.key.match(/^[0-9]$/)) {
|
|
6077
|
+
const newValue = value + e.key;
|
|
6078
|
+
const numValue = parseInt(newValue, 10);
|
|
6079
|
+
if (numValue > 59) {
|
|
6080
|
+
const digitValue = parseInt(e.key, 10);
|
|
6081
|
+
setSecond(digitValue);
|
|
6082
|
+
onChange({ hour, minute, second: digitValue });
|
|
6083
|
+
return;
|
|
6084
|
+
}
|
|
6085
|
+
if (numValue >= 0 && numValue <= 59) {
|
|
6086
|
+
setSecond(numValue);
|
|
6087
|
+
onChange({ hour, minute, second: numValue });
|
|
6088
|
+
}
|
|
6089
|
+
}
|
|
6090
|
+
}
|
|
6091
|
+
};
|
|
6092
|
+
const handleClear = () => {
|
|
6093
|
+
setHour(null);
|
|
6094
|
+
setMinute(null);
|
|
6095
|
+
setSecond(null);
|
|
6096
|
+
onChange({ hour: null, minute: null, second: null });
|
|
6097
|
+
hourInputRef.current?.focus();
|
|
6098
|
+
};
|
|
6099
|
+
return (jsxRuntime.jsx(react.Flex, { direction: "column", gap: 3, children: jsxRuntime.jsxs(react.Grid, { justifyContent: "center", alignItems: "center", templateColumns: "60px 10px 60px 10px 60px auto", gap: "2", width: "auto", minWidth: "300px", children: [jsxRuntime.jsx(react.Input, { ref: hourInputRef, type: "text", value: hour === null ? "" : hour.toString().padStart(2, "0"), onKeyDown: (e) => handleKeyDown(e, "hour"), placeholder: "HH", maxLength: 2, textAlign: "center" }), jsxRuntime.jsx(react.Text, { children: ":" }), jsxRuntime.jsx(react.Input, { ref: minuteInputRef, type: "text", value: minute === null ? "" : minute.toString().padStart(2, "0"), onKeyDown: (e) => handleKeyDown(e, "minute"), placeholder: "MM", maxLength: 2, textAlign: "center" }), jsxRuntime.jsx(react.Text, { children: ":" }), jsxRuntime.jsx(react.Input, { ref: secondInputRef, type: "text", value: second === null ? "" : second.toString().padStart(2, "0"), onKeyDown: (e) => handleKeyDown(e, "second"), placeholder: "SS", maxLength: 2, textAlign: "center" }), jsxRuntime.jsx(react.Button, { onClick: handleClear, size: "sm", variant: "ghost", children: jsxRuntime.jsx(md.MdCancel, {}) })] }) }));
|
|
6100
|
+
}
|
|
6101
|
+
|
|
6102
|
+
function DateTimePicker$1({ value, onChange, format = "date-time", showSeconds = false, labels = {
|
|
6103
|
+
monthNamesShort: [
|
|
6104
|
+
"Jan",
|
|
6105
|
+
"Feb",
|
|
6106
|
+
"Mar",
|
|
6107
|
+
"Apr",
|
|
6108
|
+
"May",
|
|
6109
|
+
"Jun",
|
|
6110
|
+
"Jul",
|
|
6111
|
+
"Aug",
|
|
6112
|
+
"Sep",
|
|
6113
|
+
"Oct",
|
|
6114
|
+
"Nov",
|
|
6115
|
+
"Dec",
|
|
6116
|
+
],
|
|
6117
|
+
weekdayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
|
|
6118
|
+
backButtonLabel: "Back",
|
|
6119
|
+
forwardButtonLabel: "Next",
|
|
6120
|
+
}, timezone = "Asia/Hong_Kong", }) {
|
|
6121
|
+
const [selectedDate, setSelectedDate] = React.useState(value || "");
|
|
6122
|
+
// Time state for 12-hour format
|
|
6123
|
+
const [hour12, setHour12] = React.useState(value ? dayjs(value).hour() % 12 || 12 : null);
|
|
6124
|
+
const [minute, setMinute] = React.useState(value ? dayjs(value).minute() : null);
|
|
6125
|
+
const [meridiem, setMeridiem] = React.useState(value ? (dayjs(value).hour() >= 12 ? "pm" : "am") : null);
|
|
6126
|
+
// Time state for 24-hour format
|
|
6127
|
+
const [hour24, setHour24] = React.useState(value ? dayjs(value).hour() : null);
|
|
6128
|
+
const [second, setSecond] = React.useState(value ? dayjs(value).second() : null);
|
|
6129
|
+
const handleDateChange = (date) => {
|
|
6130
|
+
setSelectedDate(date);
|
|
6131
|
+
updateDateTime(dayjs(date).tz(timezone).toISOString());
|
|
6132
|
+
};
|
|
6133
|
+
const handleTimeChange = (timeData) => {
|
|
6134
|
+
if (format === "iso-date-time") {
|
|
6135
|
+
setHour24(timeData.hour);
|
|
6136
|
+
setMinute(timeData.minute);
|
|
6137
|
+
if (showSeconds)
|
|
6138
|
+
setSecond(timeData.second);
|
|
6139
|
+
}
|
|
6140
|
+
else {
|
|
6141
|
+
setHour12(timeData.hour);
|
|
6142
|
+
setMinute(timeData.minute);
|
|
6143
|
+
setMeridiem(timeData.meridiem);
|
|
6144
|
+
}
|
|
6145
|
+
updateDateTime(dayjs(selectedDate).tz(timezone).toISOString(), timeData);
|
|
6146
|
+
};
|
|
6147
|
+
const updateDateTime = (date, timeData) => {
|
|
6148
|
+
if (!date) {
|
|
6149
|
+
onChange?.(undefined);
|
|
6150
|
+
return;
|
|
6151
|
+
}
|
|
6152
|
+
// use dayjs to convert the date to the timezone
|
|
6153
|
+
const newDate = dayjs(date).tz(timezone).toDate();
|
|
6154
|
+
if (format === "iso-date-time") {
|
|
6155
|
+
const h = timeData?.hour ?? hour24;
|
|
6156
|
+
const m = timeData?.minute ?? minute;
|
|
6157
|
+
const s = showSeconds ? timeData?.second ?? second : 0;
|
|
6158
|
+
if (h !== null)
|
|
6159
|
+
newDate.setHours(h);
|
|
6160
|
+
if (m !== null)
|
|
6161
|
+
newDate.setMinutes(m);
|
|
6162
|
+
if (s !== null)
|
|
6163
|
+
newDate.setSeconds(s);
|
|
6164
|
+
}
|
|
6165
|
+
else {
|
|
6166
|
+
const h = timeData?.hour ?? hour12;
|
|
6167
|
+
const m = timeData?.minute ?? minute;
|
|
6168
|
+
const mer = timeData?.meridiem ?? meridiem;
|
|
6169
|
+
if (h !== null && mer !== null) {
|
|
6170
|
+
let hour24 = h;
|
|
6171
|
+
if (mer === "am" && h === 12)
|
|
6172
|
+
hour24 = 0;
|
|
6173
|
+
else if (mer === "pm" && h < 12)
|
|
6174
|
+
hour24 = h + 12;
|
|
6175
|
+
newDate.setHours(hour24);
|
|
6176
|
+
}
|
|
6177
|
+
if (m !== null)
|
|
6178
|
+
newDate.setMinutes(m);
|
|
6179
|
+
newDate.setSeconds(0);
|
|
6180
|
+
}
|
|
6181
|
+
onChange?.(dayjs(newDate).tz(timezone).toISOString());
|
|
6182
|
+
};
|
|
6183
|
+
const handleClear = () => {
|
|
6184
|
+
setSelectedDate("");
|
|
6185
|
+
setHour12(null);
|
|
6186
|
+
setHour24(null);
|
|
6187
|
+
setMinute(null);
|
|
6188
|
+
setSecond(null);
|
|
6189
|
+
setMeridiem(null);
|
|
6190
|
+
onChange?.(undefined);
|
|
6191
|
+
};
|
|
6192
|
+
const isISO = format === "iso-date-time";
|
|
6193
|
+
return (jsxRuntime.jsxs(react.Flex, { direction: "column", gap: 4, p: 4, border: "1px solid", borderColor: "gray.200", borderRadius: "md", children: [jsxRuntime.jsx(DatePicker$1, { selected: selectedDate
|
|
6194
|
+
? dayjs(selectedDate).tz(timezone).toDate()
|
|
6195
|
+
: new Date(), onDateSelected: ({ date }) => handleDateChange(dayjs(date).tz(timezone).toISOString()), monthsToDisplay: 1, labels: labels }), jsxRuntime.jsxs(react.Grid, { templateColumns: "1fr auto", alignItems: "center", gap: 4, children: [isISO ? (jsxRuntime.jsx(IsoTimePicker, { hour: hour24, setHour: setHour24, minute: minute, setMinute: setMinute, second: second, setSecond: setSecond, onChange: handleTimeChange })) : (jsxRuntime.jsx(TimePicker$1, { hour: hour12, setHour: setHour12, minute: minute, setMinute: setMinute, meridiem: meridiem, setMeridiem: setMeridiem, onChange: handleTimeChange })), jsxRuntime.jsx(react.Button, { onClick: handleClear, size: "sm", variant: "outline", colorScheme: "red", children: jsxRuntime.jsx(react.Icon, { as: fa6.FaTrash }) })] }), selectedDate && (jsxRuntime.jsxs(react.Flex, { gap: 2, children: [jsxRuntime.jsx(react.Text, { fontSize: "sm", color: { base: "gray.600", _dark: "gray.600" }, children: dayjs(value).format(isISO
|
|
6196
|
+
? showSeconds
|
|
6197
|
+
? "YYYY-MM-DD HH:mm:ss"
|
|
6198
|
+
: "YYYY-MM-DD HH:mm"
|
|
6199
|
+
: "YYYY-MM-DD hh:mm A ") }), jsxRuntime.jsx(react.Text, { fontSize: "sm", color: { base: "gray.600", _dark: "gray.600" }, children: dayjs(value).tz(timezone).format("Z") }), jsxRuntime.jsx(react.Text, { fontSize: "sm", color: { base: "gray.600", _dark: "gray.600" }, children: timezone })] }))] }));
|
|
6200
|
+
}
|
|
6201
|
+
|
|
6202
|
+
dayjs.extend(utc);
|
|
6203
|
+
dayjs.extend(timezone);
|
|
6204
|
+
const DateTimePicker = ({ column, schema, prefix, }) => {
|
|
6205
|
+
const { watch, formState: { errors }, setValue, } = reactHookForm.useFormContext();
|
|
6206
|
+
const { timezone, dateTimePickerLabels } = useSchemaContext();
|
|
6207
|
+
const formI18n = useFormI18n(column, prefix);
|
|
6208
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1', displayDateFormat = 'YYYY-MM-DD HH:mm:ss',
|
|
6209
|
+
// with timezone
|
|
6210
|
+
dateFormat = 'YYYY-MM-DD[T]HH:mm:ssZ', } = schema;
|
|
6211
|
+
const isRequired = required?.some((columnId) => columnId === column);
|
|
6212
|
+
const colLabel = formI18n.colLabel;
|
|
6213
|
+
const [open, setOpen] = React.useState(false);
|
|
6214
|
+
const selectedDate = watch(colLabel);
|
|
6215
|
+
const displayDate = dayjs(selectedDate)
|
|
6216
|
+
.tz(timezone)
|
|
6217
|
+
.format(displayDateFormat);
|
|
6218
|
+
React.useEffect(() => {
|
|
6219
|
+
try {
|
|
6220
|
+
if (selectedDate) {
|
|
6221
|
+
// Parse the selectedDate as UTC or in a specific timezone to avoid +8 hour shift
|
|
6222
|
+
// For example, parse as UTC:
|
|
6223
|
+
const parsedDate = dayjs(selectedDate).tz(timezone);
|
|
6224
|
+
if (!parsedDate.isValid())
|
|
6225
|
+
return;
|
|
6226
|
+
// Format according to dateFormat from schema
|
|
6227
|
+
const formatted = parsedDate.format(dateFormat);
|
|
6228
|
+
// Update the form value only if different to avoid loops
|
|
6229
|
+
if (formatted !== selectedDate) {
|
|
6230
|
+
setValue(colLabel, formatted, {
|
|
6231
|
+
shouldValidate: true,
|
|
6232
|
+
shouldDirty: true,
|
|
6233
|
+
});
|
|
6234
|
+
}
|
|
6235
|
+
}
|
|
6236
|
+
}
|
|
6237
|
+
catch (e) {
|
|
6238
|
+
console.error(e);
|
|
6239
|
+
}
|
|
6240
|
+
}, [selectedDate, dateFormat, colLabel, setValue]);
|
|
6241
|
+
return (jsxRuntime.jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
6242
|
+
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: jsxRuntime.jsxs(PopoverRoot, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, children: [jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: jsxRuntime.jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
|
|
6243
|
+
setOpen(true);
|
|
6244
|
+
}, justifyContent: 'start', children: [jsxRuntime.jsx(md.MdDateRange, {}), selectedDate !== undefined ? `${displayDate}` : ''] }) }), jsxRuntime.jsx(PopoverContent, { minW: '450px', children: jsxRuntime.jsxs(PopoverBody, { children: [jsxRuntime.jsx(PopoverTitle, {}), jsxRuntime.jsx(DateTimePicker$1, { value: selectedDate, onChange: (date) => {
|
|
6245
|
+
setValue(colLabel, dayjs(date).tz(timezone).format(dateFormat));
|
|
6246
|
+
}, timezone: timezone, labels: {
|
|
6247
|
+
monthNamesShort: dateTimePickerLabels?.monthNamesShort ?? [
|
|
6248
|
+
formI18n.translate.t(`common.month_1`, {
|
|
6249
|
+
defaultValue: 'January',
|
|
6250
|
+
}),
|
|
6251
|
+
formI18n.translate.t(`common.month_2`, {
|
|
6252
|
+
defaultValue: 'February',
|
|
6253
|
+
}),
|
|
6254
|
+
formI18n.translate.t(`common.month_3`, {
|
|
6255
|
+
defaultValue: 'March',
|
|
6256
|
+
}),
|
|
6257
|
+
formI18n.translate.t(`common.month_4`, {
|
|
6258
|
+
defaultValue: 'April',
|
|
6259
|
+
}),
|
|
6260
|
+
formI18n.translate.t(`common.month_5`, {
|
|
6261
|
+
defaultValue: 'May',
|
|
6262
|
+
}),
|
|
6263
|
+
formI18n.translate.t(`common.month_6`, {
|
|
6264
|
+
defaultValue: 'June',
|
|
6265
|
+
}),
|
|
6266
|
+
formI18n.translate.t(`common.month_7`, {
|
|
6267
|
+
defaultValue: 'July',
|
|
6268
|
+
}),
|
|
6269
|
+
formI18n.translate.t(`common.month_8`, {
|
|
6270
|
+
defaultValue: 'August',
|
|
6271
|
+
}),
|
|
6272
|
+
formI18n.translate.t(`common.month_9`, {
|
|
6273
|
+
defaultValue: 'September',
|
|
6274
|
+
}),
|
|
6275
|
+
formI18n.translate.t(`common.month_10`, {
|
|
6276
|
+
defaultValue: 'October',
|
|
6277
|
+
}),
|
|
6278
|
+
formI18n.translate.t(`common.month_11`, {
|
|
6279
|
+
defaultValue: 'November',
|
|
6280
|
+
}),
|
|
6281
|
+
formI18n.translate.t(`common.month_12`, {
|
|
6282
|
+
defaultValue: 'December',
|
|
6283
|
+
}),
|
|
6284
|
+
],
|
|
6285
|
+
weekdayNamesShort: dateTimePickerLabels?.weekdayNamesShort ?? [
|
|
6286
|
+
formI18n.translate.t(`common.weekday_1`, {
|
|
6287
|
+
defaultValue: 'Sun',
|
|
6288
|
+
}),
|
|
6289
|
+
formI18n.translate.t(`common.weekday_2`, {
|
|
6290
|
+
defaultValue: 'Mon',
|
|
6291
|
+
}),
|
|
6292
|
+
formI18n.translate.t(`common.weekday_3`, {
|
|
6293
|
+
defaultValue: 'Tue',
|
|
6294
|
+
}),
|
|
6295
|
+
formI18n.translate.t(`common.weekday_4`, {
|
|
6296
|
+
defaultValue: 'Wed',
|
|
6297
|
+
}),
|
|
6298
|
+
formI18n.translate.t(`common.weekday_5`, {
|
|
6299
|
+
defaultValue: 'Thu',
|
|
6300
|
+
}),
|
|
6301
|
+
formI18n.translate.t(`common.weekday_6`, {
|
|
6302
|
+
defaultValue: 'Fri',
|
|
6303
|
+
}),
|
|
6304
|
+
formI18n.translate.t(`common.weekday_7`, {
|
|
6305
|
+
defaultValue: 'Sat',
|
|
6306
|
+
}),
|
|
6307
|
+
],
|
|
6308
|
+
backButtonLabel: dateTimePickerLabels?.backButtonLabel ??
|
|
6309
|
+
formI18n.translate.t(`common.back_button`, {
|
|
6310
|
+
defaultValue: 'Back',
|
|
6311
|
+
}),
|
|
6312
|
+
forwardButtonLabel: dateTimePickerLabels?.forwardButtonLabel ??
|
|
6313
|
+
formI18n.translate.t(`common.forward_button`, {
|
|
6314
|
+
defaultValue: 'Forward',
|
|
6315
|
+
}),
|
|
6316
|
+
} })] }) })] }) }));
|
|
6317
|
+
};
|
|
6318
|
+
|
|
4603
6319
|
const SchemaRenderer = ({ schema, prefix, column, }) => {
|
|
4604
6320
|
const colSchema = schema;
|
|
4605
|
-
const { type, variant, properties: innerProperties, foreign_key, items, } = schema;
|
|
4606
|
-
if (
|
|
6321
|
+
const { type, variant, properties: innerProperties, foreign_key, format, items, } = schema;
|
|
6322
|
+
if (variant === 'custom-input') {
|
|
6323
|
+
return jsxRuntime.jsx(CustomInput, { schema: colSchema, prefix, column });
|
|
6324
|
+
}
|
|
6325
|
+
if (type === 'string') {
|
|
4607
6326
|
if ((schema.enum ?? []).length > 0) {
|
|
4608
6327
|
return jsxRuntime.jsx(EnumPicker, { schema: colSchema, prefix, column });
|
|
4609
6328
|
}
|
|
4610
|
-
if (variant ===
|
|
6329
|
+
if (variant === 'id-picker') {
|
|
4611
6330
|
idPickerSanityCheck(column, foreign_key);
|
|
4612
6331
|
return jsxRuntime.jsx(IdPicker, { schema: colSchema, prefix, column });
|
|
4613
6332
|
}
|
|
4614
|
-
if (
|
|
6333
|
+
if (format === 'date') {
|
|
4615
6334
|
return jsxRuntime.jsx(DatePicker, { schema: colSchema, prefix, column });
|
|
4616
6335
|
}
|
|
6336
|
+
if (format === 'time') {
|
|
6337
|
+
return jsxRuntime.jsx(TimePicker, { schema: colSchema, prefix, column });
|
|
6338
|
+
}
|
|
6339
|
+
if (format === 'date-time') {
|
|
6340
|
+
return jsxRuntime.jsx(DateTimePicker, { schema: colSchema, prefix, column });
|
|
6341
|
+
}
|
|
6342
|
+
if (variant === 'text-area') {
|
|
6343
|
+
return jsxRuntime.jsx(TextAreaInput, { schema: colSchema, prefix, column });
|
|
6344
|
+
}
|
|
4617
6345
|
return jsxRuntime.jsx(StringInputField, { schema: colSchema, prefix, column });
|
|
4618
6346
|
}
|
|
4619
|
-
if (type ===
|
|
6347
|
+
if (type === 'number' || type === 'integer') {
|
|
4620
6348
|
return jsxRuntime.jsx(NumberInputField, { schema: colSchema, prefix, column });
|
|
4621
6349
|
}
|
|
4622
|
-
if (type ===
|
|
6350
|
+
if (type === 'boolean') {
|
|
4623
6351
|
return jsxRuntime.jsx(BooleanPicker, { schema: colSchema, prefix, column });
|
|
4624
6352
|
}
|
|
4625
|
-
if (type ===
|
|
6353
|
+
if (type === 'object') {
|
|
4626
6354
|
if (innerProperties) {
|
|
4627
6355
|
return jsxRuntime.jsx(ObjectInput, { schema: colSchema, prefix, column });
|
|
4628
6356
|
}
|
|
4629
6357
|
return jsxRuntime.jsx(RecordInput$1, { schema: colSchema, prefix, column });
|
|
4630
6358
|
}
|
|
4631
|
-
if (type ===
|
|
4632
|
-
if (variant ===
|
|
6359
|
+
if (type === 'array') {
|
|
6360
|
+
if (variant === 'id-picker') {
|
|
4633
6361
|
idPickerSanityCheck(column, foreign_key);
|
|
4634
6362
|
return (jsxRuntime.jsx(IdPicker, { schema: colSchema, prefix, column, isMultiple: true }));
|
|
4635
6363
|
}
|
|
4636
|
-
if (variant ===
|
|
6364
|
+
if (variant === 'tag-picker') {
|
|
4637
6365
|
return jsxRuntime.jsx(TagPicker, { schema: colSchema, prefix, column });
|
|
4638
6366
|
}
|
|
4639
|
-
if (variant ===
|
|
6367
|
+
if (variant === 'file-picker') {
|
|
4640
6368
|
return jsxRuntime.jsx(FilePicker, { schema: colSchema, prefix, column });
|
|
4641
6369
|
}
|
|
6370
|
+
if (variant === 'date-range') {
|
|
6371
|
+
return jsxRuntime.jsx(DateRangePicker, { schema: colSchema, prefix, column });
|
|
6372
|
+
}
|
|
6373
|
+
if (variant === 'enum-picker') {
|
|
6374
|
+
const { items } = colSchema;
|
|
6375
|
+
const { enum: enumItems } = items;
|
|
6376
|
+
const enumSchema = {
|
|
6377
|
+
type: 'string',
|
|
6378
|
+
enum: enumItems,
|
|
6379
|
+
};
|
|
6380
|
+
return (jsxRuntime.jsx(EnumPicker, { isMultiple: true, schema: enumSchema, prefix, column }));
|
|
6381
|
+
}
|
|
4642
6382
|
if (items) {
|
|
4643
6383
|
return jsxRuntime.jsx(ArrayRenderer, { schema: colSchema, prefix, column });
|
|
4644
6384
|
}
|
|
4645
6385
|
return jsxRuntime.jsx(react.Text, { children: `array ${column}` });
|
|
4646
6386
|
}
|
|
4647
|
-
if (type ===
|
|
6387
|
+
if (type === 'null') {
|
|
4648
6388
|
return jsxRuntime.jsx(react.Text, { children: `null ${column}` });
|
|
4649
6389
|
}
|
|
4650
6390
|
return jsxRuntime.jsx(react.Text, { children: "missing type" });
|
|
4651
6391
|
};
|
|
4652
6392
|
|
|
4653
|
-
const ColumnRenderer = ({ column, properties, prefix, }) => {
|
|
6393
|
+
const ColumnRenderer = ({ column, properties, prefix, parentRequired, }) => {
|
|
4654
6394
|
const colSchema = properties[column];
|
|
4655
6395
|
const colLabel = `${prefix}${column}`;
|
|
4656
6396
|
if (colSchema === undefined) {
|
|
4657
6397
|
throw new Error(`${colLabel} does not exist when using ColumnRenderer`);
|
|
4658
6398
|
}
|
|
4659
|
-
|
|
6399
|
+
// Merge parent's required array with the schema's required array
|
|
6400
|
+
const schemaWithRequired = {
|
|
6401
|
+
...colSchema,
|
|
6402
|
+
required: parentRequired || colSchema.required,
|
|
6403
|
+
};
|
|
6404
|
+
return jsxRuntime.jsx(SchemaRenderer, { schema: schemaWithRequired, prefix, column });
|
|
4660
6405
|
};
|
|
4661
6406
|
|
|
4662
6407
|
const ArrayViewer = ({ schema, column, prefix }) => {
|
|
4663
|
-
const {
|
|
6408
|
+
const { gridColumn = "span 12", gridRow = "span 1", required, items, } = schema;
|
|
4664
6409
|
const { translate } = useSchemaContext();
|
|
4665
6410
|
const colLabel = `${prefix}${column}`;
|
|
4666
6411
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4667
6412
|
const { watch, formState: { errors }, } = reactHookForm.useFormContext();
|
|
4668
6413
|
const values = watch(colLabel) ?? [];
|
|
4669
|
-
return (jsxRuntime.jsxs(react.Box, { gridRow, gridColumn, children: [jsxRuntime.jsxs(react.Box, { as: "label", gridColumn: "1/span12", children: [`${translate.t(removeIndex(`${colLabel}.
|
|
4670
|
-
|
|
4671
|
-
|
|
6414
|
+
return (jsxRuntime.jsxs(react.Box, { gridRow, gridColumn, children: [jsxRuntime.jsxs(react.Box, { as: "label", gridColumn: "1/span12", children: [`${translate.t(removeIndex(`${colLabel}.field_label`))}`, isRequired && jsxRuntime.jsx("span", { children: "*" })] }), jsxRuntime.jsx(react.Flex, { flexFlow: "column", gap: 1, children: values.map((field, index) => (jsxRuntime.jsx(react.Flex, { flexFlow: "column", bgColor: { base: "colorPalette.100", _dark: "colorPalette.900" }, p: "2", borderRadius: "md", borderWidth: "thin", borderColor: {
|
|
6415
|
+
base: "colorPalette.200",
|
|
6416
|
+
_dark: "colorPalette.800",
|
|
6417
|
+
}, children: jsxRuntime.jsx(react.Grid, { gap: "4", gridTemplateColumns: "repeat(12, 1fr)", autoFlow: "row", children: jsxRuntime.jsx(SchemaViewer, { column: `${index}`,
|
|
6418
|
+
prefix: `${colLabel}.`,
|
|
6419
|
+
// @ts-expect-error find suitable types
|
|
6420
|
+
schema: { showLabel: false, ...(items ?? {}) } }) }) }, `form-${prefix}${column}.${index}`))) }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
|
|
4672
6421
|
};
|
|
4673
6422
|
|
|
4674
6423
|
const BooleanViewer = ({ schema, column, prefix, }) => {
|
|
4675
6424
|
const { watch, formState: { errors }, } = reactHookForm.useFormContext();
|
|
4676
6425
|
const { translate } = useSchemaContext();
|
|
4677
|
-
const { required, gridColumn, gridRow } = schema;
|
|
6426
|
+
const { required, gridColumn = "span 12", gridRow = "span 1" } = schema;
|
|
4678
6427
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4679
6428
|
const colLabel = `${prefix}${column}`;
|
|
4680
6429
|
const value = watch(colLabel);
|
|
4681
|
-
return (jsxRuntime.jsxs(Field, { label: `${translate.t(removeIndex(`${colLabel}.
|
|
6430
|
+
return (jsxRuntime.jsxs(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, alignItems: "stretch", gridColumn,
|
|
4682
6431
|
gridRow, children: [jsxRuntime.jsx(react.Text, { children: value
|
|
4683
6432
|
? translate.t(removeIndex(`${colLabel}.true`))
|
|
4684
|
-
: translate.t(removeIndex(`${colLabel}.false`)) }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.
|
|
6433
|
+
: translate.t(removeIndex(`${colLabel}.false`)) }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
|
|
6434
|
+
};
|
|
6435
|
+
|
|
6436
|
+
const CustomViewer = ({ column, schema, prefix }) => {
|
|
6437
|
+
const formContext = reactHookForm.useFormContext();
|
|
6438
|
+
const { inputViewerRender } = schema;
|
|
6439
|
+
return (inputViewerRender &&
|
|
6440
|
+
inputViewerRender({
|
|
6441
|
+
column,
|
|
6442
|
+
schema,
|
|
6443
|
+
prefix,
|
|
6444
|
+
formContext,
|
|
6445
|
+
}));
|
|
4685
6446
|
};
|
|
4686
6447
|
|
|
4687
6448
|
const DateViewer = ({ column, schema, prefix }) => {
|
|
4688
6449
|
const { watch, formState: { errors }, } = reactHookForm.useFormContext();
|
|
4689
|
-
const { translate } = useSchemaContext();
|
|
4690
|
-
const { required, gridColumn, gridRow } = schema;
|
|
6450
|
+
const { translate, timezone } = useSchemaContext();
|
|
6451
|
+
const { required, gridColumn = "span 4", gridRow = "span 1", displayDateFormat = "YYYY-MM-DD", } = schema;
|
|
4691
6452
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4692
6453
|
const colLabel = `${prefix}${column}`;
|
|
4693
6454
|
const selectedDate = watch(colLabel);
|
|
4694
|
-
|
|
4695
|
-
|
|
6455
|
+
const displayDate = dayjs(selectedDate).tz(timezone).format(displayDateFormat);
|
|
6456
|
+
return (jsxRuntime.jsxs(Field, { label: `${translate.t(removeIndex(`${column}.field_label`))}`, required: isRequired, alignItems: "stretch", gridColumn,
|
|
6457
|
+
gridRow, children: [jsxRuntime.jsxs(react.Text, { children: [" ", selectedDate !== undefined ? displayDate : ""] }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: translate.t(`${column}.field_required`) }))] }));
|
|
4696
6458
|
};
|
|
4697
6459
|
|
|
4698
6460
|
const EnumViewer = ({ column, isMultiple = false, schema, prefix, }) => {
|
|
4699
6461
|
const { watch, formState: { errors }, } = reactHookForm.useFormContext();
|
|
4700
|
-
const
|
|
6462
|
+
const formI18n = useFormI18n(column, prefix);
|
|
4701
6463
|
const { required } = schema;
|
|
4702
6464
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4703
|
-
const { gridColumn, gridRow, renderDisplay } = schema;
|
|
4704
|
-
const colLabel =
|
|
6465
|
+
const { gridColumn = "span 12", gridRow = "span 1", renderDisplay } = schema;
|
|
6466
|
+
const colLabel = formI18n.colLabel;
|
|
4705
6467
|
const watchEnum = watch(colLabel);
|
|
4706
6468
|
const watchEnums = (watch(colLabel) ?? []);
|
|
4707
|
-
return (jsxRuntime.jsxs(Field, { label:
|
|
6469
|
+
return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: "stretch", gridColumn,
|
|
4708
6470
|
gridRow, children: [isMultiple && (jsxRuntime.jsx(react.Flex, { flexFlow: "wrap", gap: 1, children: watchEnums.map((enumValue) => {
|
|
4709
6471
|
const item = enumValue;
|
|
4710
6472
|
if (item === undefined) {
|
|
4711
6473
|
return jsxRuntime.jsx(jsxRuntime.Fragment, { children: "undefined" });
|
|
4712
6474
|
}
|
|
4713
|
-
return (jsxRuntime.jsx(Tag, {
|
|
6475
|
+
return (jsxRuntime.jsx(Tag, { size: "lg", children: !!renderDisplay === true
|
|
4714
6476
|
? renderDisplay(item)
|
|
4715
|
-
:
|
|
4716
|
-
}) })), !isMultiple &&
|
|
6477
|
+
: formI18n.t(item) }, item));
|
|
6478
|
+
}) })), !isMultiple && jsxRuntime.jsx(react.Text, { children: formI18n.t(watchEnum) }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: formI18n.required() }))] }));
|
|
4717
6479
|
};
|
|
4718
6480
|
|
|
4719
6481
|
const FileViewer = ({ column, schema, prefix }) => {
|
|
4720
|
-
const {
|
|
6482
|
+
const { watch } = reactHookForm.useFormContext();
|
|
4721
6483
|
const { translate } = useSchemaContext();
|
|
4722
|
-
const { required, gridColumn, gridRow } = schema;
|
|
6484
|
+
const { required, gridColumn = "span 12", gridRow = "span 1", } = schema;
|
|
4723
6485
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4724
6486
|
const currentFiles = (watch(column) ?? []);
|
|
4725
6487
|
const colLabel = `${prefix}${column}`;
|
|
4726
|
-
return (jsxRuntime.
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
}, placeholder: translate.t(`${colLabel}.fileDropzone`) }), jsxRuntime.jsx(react.Flex, { flexFlow: "column", gap: 1, children: currentFiles.map((file) => {
|
|
4730
|
-
return (jsxRuntime.jsx(react.Card.Root, { variant: "subtle", children: jsxRuntime.jsxs(react.Card.Body, { gap: "2", cursor: "pointer", onClick: () => {
|
|
4731
|
-
setValue(column, currentFiles.filter(({ name }) => {
|
|
4732
|
-
return name !== file.name;
|
|
4733
|
-
}));
|
|
4734
|
-
}, display: "flex", flexFlow: "row", alignItems: "center", padding: "2", children: [jsxRuntime.jsx(react.Box, { children: file.name }), jsxRuntime.jsx(ti.TiDeleteOutline, {})] }) }, file.name));
|
|
4735
|
-
}) }), errors[`${colLabel}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.fieldRequired`)) }))] }));
|
|
6488
|
+
return (jsxRuntime.jsx(Field, { label: `${translate.t(`${colLabel}.field_label`)}`, required: isRequired, gridColumn: gridColumn, gridRow: gridRow, display: "grid", gridTemplateRows: "auto 1fr auto", alignItems: "stretch", children: jsxRuntime.jsx(react.Flex, { flexFlow: "column", gap: 1, children: currentFiles.map((file) => {
|
|
6489
|
+
return (jsxRuntime.jsx(react.Card.Root, { variant: "subtle", children: jsxRuntime.jsxs(react.Card.Body, { gap: "2", display: "flex", flexFlow: "row", alignItems: "center", padding: "2", children: [file.type.startsWith("image/") && (jsxRuntime.jsx(react.Image, { src: URL.createObjectURL(file), alt: file.name, boxSize: "50px", objectFit: "cover", borderRadius: "md", marginRight: "2" })), jsxRuntime.jsx(react.Box, { children: file.name })] }) }, file.name));
|
|
6490
|
+
}) }) }));
|
|
4736
6491
|
};
|
|
4737
6492
|
|
|
4738
6493
|
const IdViewer = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
4739
6494
|
const { watch, formState: { errors }, } = reactHookForm.useFormContext();
|
|
4740
6495
|
const { idMap, translate } = useSchemaContext();
|
|
4741
|
-
const { required, gridColumn, gridRow, renderDisplay, foreign_key } = schema;
|
|
6496
|
+
const { required, gridColumn = "span 12", gridRow = "span 1", renderDisplay, foreign_key, } = schema;
|
|
4742
6497
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4743
6498
|
const { display_column } = foreign_key;
|
|
4744
6499
|
const colLabel = `${prefix}${column}`;
|
|
@@ -4754,7 +6509,7 @@ const IdViewer = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
4754
6509
|
}
|
|
4755
6510
|
return record[display_column];
|
|
4756
6511
|
};
|
|
4757
|
-
return (jsxRuntime.jsxs(Field, { label: `${translate.t(removeIndex(`${column}.
|
|
6512
|
+
return (jsxRuntime.jsxs(Field, { label: `${translate.t(removeIndex(`${column}.field_label`))}`, required: isRequired, alignItems: "stretch", gridColumn,
|
|
4758
6513
|
gridRow, children: [isMultiple && (jsxRuntime.jsx(react.Flex, { flexFlow: "wrap", gap: 1, children: watchIds.map((id) => {
|
|
4759
6514
|
const item = idMap[id];
|
|
4760
6515
|
if (item === undefined) {
|
|
@@ -4763,21 +6518,39 @@ const IdViewer = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
4763
6518
|
return (jsxRuntime.jsx(Tag, { closable: true, children: !!renderDisplay === true
|
|
4764
6519
|
? renderDisplay(item)
|
|
4765
6520
|
: item[display_column] }, id));
|
|
4766
|
-
}) })), !isMultiple && jsxRuntime.jsx(react.Text, { children: getPickedValue() }), errors[`${colLabel}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.
|
|
6521
|
+
}) })), !isMultiple && jsxRuntime.jsx(react.Text, { children: getPickedValue() }), errors[`${colLabel}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
|
|
4767
6522
|
};
|
|
4768
6523
|
|
|
4769
6524
|
const NumberViewer = ({ schema, column, prefix, }) => {
|
|
4770
6525
|
const { watch, formState: { errors }, } = reactHookForm.useFormContext();
|
|
4771
6526
|
const { translate } = useSchemaContext();
|
|
4772
|
-
const { required, gridColumn, gridRow } = schema;
|
|
6527
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1' } = schema;
|
|
4773
6528
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4774
6529
|
const colLabel = `${prefix}${column}`;
|
|
4775
6530
|
const value = watch(colLabel);
|
|
4776
|
-
|
|
6531
|
+
// Format the value for display if formatOptions are provided
|
|
6532
|
+
const formatValue = (val) => {
|
|
6533
|
+
if (val === undefined || val === null || val === '')
|
|
6534
|
+
return '';
|
|
6535
|
+
const numValue = typeof val === 'string' ? parseFloat(val) : val;
|
|
6536
|
+
if (isNaN(numValue))
|
|
6537
|
+
return String(val);
|
|
6538
|
+
// Use formatOptions if available, otherwise display as-is
|
|
6539
|
+
if (schema.formatOptions) {
|
|
6540
|
+
try {
|
|
6541
|
+
return new Intl.NumberFormat(undefined, schema.formatOptions).format(numValue);
|
|
6542
|
+
}
|
|
6543
|
+
catch {
|
|
6544
|
+
return String(val);
|
|
6545
|
+
}
|
|
6546
|
+
}
|
|
6547
|
+
return String(val);
|
|
6548
|
+
};
|
|
6549
|
+
return (jsxRuntime.jsxs(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, gridColumn, gridRow, children: [jsxRuntime.jsx(react.Text, { children: formatValue(value) }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: 'red.400', children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
|
|
4777
6550
|
};
|
|
4778
6551
|
|
|
4779
6552
|
const ObjectViewer = ({ schema, column, prefix }) => {
|
|
4780
|
-
const { properties,
|
|
6553
|
+
const { properties, gridColumn = "span 12", gridRow = "span 1", required, showLabel = true, } = schema;
|
|
4781
6554
|
const { translate } = useSchemaContext();
|
|
4782
6555
|
const colLabel = `${prefix}${column}`;
|
|
4783
6556
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
@@ -4785,25 +6558,28 @@ const ObjectViewer = ({ schema, column, prefix }) => {
|
|
|
4785
6558
|
if (properties === undefined) {
|
|
4786
6559
|
throw new Error(`properties is undefined when using ObjectInput`);
|
|
4787
6560
|
}
|
|
4788
|
-
return (jsxRuntime.jsxs(react.Box, { gridRow, gridColumn, children: [jsxRuntime.jsxs(react.Box, { as: "label",
|
|
6561
|
+
return (jsxRuntime.jsxs(react.Box, { gridRow, gridColumn, children: [showLabel && (jsxRuntime.jsxs(react.Box, { as: "label", children: [`${translate.t(removeIndex(`${colLabel}.field_label`))}`, isRequired && jsxRuntime.jsx("span", { children: "*" })] })), jsxRuntime.jsx(react.Grid, { gap: "4", padding: "4", gridTemplateColumns: "repeat(12, 1fr)", autoFlow: "row", bgColor: { base: "colorPalette.100", _dark: "colorPalette.900" }, p: "1", borderRadius: "md", borderWidth: "thin", borderColor: {
|
|
6562
|
+
base: "colorPalette.200",
|
|
6563
|
+
_dark: "colorPalette.800",
|
|
6564
|
+
}, children: Object.keys(properties ?? {}).map((key) => {
|
|
4789
6565
|
return (
|
|
4790
6566
|
// @ts-expect-error find suitable types
|
|
4791
6567
|
jsxRuntime.jsx(ColumnViewer, { column: `${key}`,
|
|
4792
6568
|
prefix: `${prefix}${column}.`,
|
|
4793
6569
|
properties }, `form-objectviewer-${colLabel}-${key}`));
|
|
4794
|
-
}) }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.
|
|
6570
|
+
}) }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
|
|
4795
6571
|
};
|
|
4796
6572
|
|
|
4797
6573
|
const RecordInput = ({ column, schema, prefix }) => {
|
|
4798
6574
|
const { formState: { errors }, setValue, getValues, } = reactHookForm.useFormContext();
|
|
4799
6575
|
const { translate } = useSchemaContext();
|
|
4800
|
-
const { required, gridColumn, gridRow } = schema;
|
|
6576
|
+
const { required, gridColumn = "span 12", gridRow = "span 1" } = schema;
|
|
4801
6577
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4802
6578
|
const entries = Object.entries(getValues(column) ?? {});
|
|
4803
6579
|
const [showNewEntries, setShowNewEntries] = React.useState(false);
|
|
4804
6580
|
const [newKey, setNewKey] = React.useState();
|
|
4805
6581
|
const [newValue, setNewValue] = React.useState();
|
|
4806
|
-
return (jsxRuntime.jsxs(Field, { label: `${translate.t(`${column}.
|
|
6582
|
+
return (jsxRuntime.jsxs(Field, { label: `${translate.t(`${column}.field_label`)}`, required: isRequired, alignItems: "stretch", gridColumn, gridRow, children: [entries.map(([key, value]) => {
|
|
4807
6583
|
return (jsxRuntime.jsxs(react.Grid, { templateColumns: "1fr 1fr auto", gap: 1, children: [jsxRuntime.jsx(react.Input, { value: key, onChange: (e) => {
|
|
4808
6584
|
const filtered = entries.filter(([target]) => {
|
|
4809
6585
|
return target !== key;
|
|
@@ -4843,7 +6619,17 @@ const RecordInput = ({ column, schema, prefix }) => {
|
|
|
4843
6619
|
setShowNewEntries(true);
|
|
4844
6620
|
setNewKey(undefined);
|
|
4845
6621
|
setNewValue(undefined);
|
|
4846
|
-
}, children: translate.t(`${column}.addNew`) }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: translate.t(`${column}.
|
|
6622
|
+
}, children: translate.t(`${column}.addNew`) }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: translate.t(`${column}.field_required`) }))] }));
|
|
6623
|
+
};
|
|
6624
|
+
|
|
6625
|
+
const StringViewer = ({ column, schema, prefix, }) => {
|
|
6626
|
+
const { watch, formState: { errors }, } = reactHookForm.useFormContext();
|
|
6627
|
+
const { translate } = useSchemaContext();
|
|
6628
|
+
const { required, gridColumn = "span 12", gridRow = "span 1" } = schema;
|
|
6629
|
+
const isRequired = required?.some((columnId) => columnId === column);
|
|
6630
|
+
const colLabel = `${prefix}${column}`;
|
|
6631
|
+
const value = watch(colLabel);
|
|
6632
|
+
return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsxs(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, gridColumn: gridColumn ?? "span 4", gridRow: gridRow ?? "span 1", children: [jsxRuntime.jsx(react.Text, { children: value }), errors[colLabel] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }) }));
|
|
4847
6633
|
};
|
|
4848
6634
|
|
|
4849
6635
|
const TagViewer = ({ column, schema, prefix }) => {
|
|
@@ -4931,19 +6717,50 @@ const TagViewer = ({ column, schema, prefix }) => {
|
|
|
4931
6717
|
}), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: (errors[`${column}`]?.message ?? "No error message") }))] }));
|
|
4932
6718
|
};
|
|
4933
6719
|
|
|
4934
|
-
const
|
|
6720
|
+
const TextAreaViewer = ({ column, schema, prefix, }) => {
|
|
4935
6721
|
const { watch, formState: { errors }, } = reactHookForm.useFormContext();
|
|
4936
6722
|
const { translate } = useSchemaContext();
|
|
4937
|
-
const { required, gridColumn, gridRow } = schema;
|
|
6723
|
+
const { required, gridColumn = "span 12", gridRow = "span 1" } = schema;
|
|
4938
6724
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4939
6725
|
const colLabel = `${prefix}${column}`;
|
|
4940
6726
|
const value = watch(colLabel);
|
|
4941
|
-
return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsxs(Field, { label: `${translate.t(removeIndex(`${colLabel}.
|
|
6727
|
+
return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsxs(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, gridColumn: gridColumn, gridRow: gridRow, children: [jsxRuntime.jsx(react.Text, { whiteSpace: "pre-wrap", children: value }), " ", errors[colLabel] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }) }));
|
|
6728
|
+
};
|
|
6729
|
+
|
|
6730
|
+
const TimeViewer = ({ column, schema, prefix }) => {
|
|
6731
|
+
const { watch, formState: { errors }, } = reactHookForm.useFormContext();
|
|
6732
|
+
const { translate, timezone } = useSchemaContext();
|
|
6733
|
+
const { required, gridColumn = "span 12", gridRow = "span 1", displayTimeFormat = "hh:mm A", } = schema;
|
|
6734
|
+
const isRequired = required?.some((columnId) => columnId === column);
|
|
6735
|
+
const colLabel = `${prefix}${column}`;
|
|
6736
|
+
const selectedDate = watch(colLabel);
|
|
6737
|
+
const displayedTime = dayjs(`1970-01-01T${selectedDate}`)
|
|
6738
|
+
.tz(timezone)
|
|
6739
|
+
.isValid()
|
|
6740
|
+
? dayjs(`1970-01-01T${selectedDate}`).tz(timezone).format(displayTimeFormat)
|
|
6741
|
+
: "";
|
|
6742
|
+
return (jsxRuntime.jsxs(Field, { label: `${translate.t(removeIndex(`${column}.field_label`))}`, required: isRequired, alignItems: "stretch", gridColumn,
|
|
6743
|
+
gridRow, children: [jsxRuntime.jsx(react.Text, { children: displayedTime }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: translate.t(`${column}.field_required`) }))] }));
|
|
6744
|
+
};
|
|
6745
|
+
|
|
6746
|
+
const DateTimeViewer = ({ column, schema, prefix }) => {
|
|
6747
|
+
const { watch, formState: { errors }, } = reactHookForm.useFormContext();
|
|
6748
|
+
const { translate, timezone } = useSchemaContext();
|
|
6749
|
+
const { required, gridColumn = "span 4", gridRow = "span 1", displayDateFormat = "YYYY-MM-DD HH:mm:ss", } = schema;
|
|
6750
|
+
const isRequired = required?.some((columnId) => columnId === column);
|
|
6751
|
+
const colLabel = `${prefix}${column}`;
|
|
6752
|
+
const selectedDate = watch(colLabel);
|
|
6753
|
+
const displayDate = dayjs(selectedDate).tz(timezone).format(displayDateFormat);
|
|
6754
|
+
return (jsxRuntime.jsxs(Field, { label: `${translate.t(removeIndex(`${column}.field_label`))}`, required: isRequired, alignItems: "stretch", gridColumn,
|
|
6755
|
+
gridRow, children: [jsxRuntime.jsxs(react.Text, { children: [" ", selectedDate !== undefined ? displayDate : ""] }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: translate.t(`${column}.field_required`) }))] }));
|
|
4942
6756
|
};
|
|
4943
6757
|
|
|
4944
6758
|
const SchemaViewer = ({ schema, prefix, column, }) => {
|
|
4945
6759
|
const colSchema = schema;
|
|
4946
|
-
const { type, variant, properties: innerProperties, foreign_key, items, } = schema;
|
|
6760
|
+
const { type, variant, properties: innerProperties, foreign_key, items, format, } = schema;
|
|
6761
|
+
if (variant === "custom-input") {
|
|
6762
|
+
return jsxRuntime.jsx(CustomViewer, { schema: colSchema, prefix, column });
|
|
6763
|
+
}
|
|
4947
6764
|
if (type === "string") {
|
|
4948
6765
|
if ((schema.enum ?? []).length > 0) {
|
|
4949
6766
|
return jsxRuntime.jsx(EnumViewer, { schema: colSchema, prefix, column });
|
|
@@ -4952,9 +6769,18 @@ const SchemaViewer = ({ schema, prefix, column, }) => {
|
|
|
4952
6769
|
idPickerSanityCheck(column, foreign_key);
|
|
4953
6770
|
return jsxRuntime.jsx(IdViewer, { schema: colSchema, prefix, column });
|
|
4954
6771
|
}
|
|
4955
|
-
if (
|
|
6772
|
+
if (format === "time") {
|
|
6773
|
+
return jsxRuntime.jsx(TimeViewer, { schema: colSchema, prefix, column });
|
|
6774
|
+
}
|
|
6775
|
+
if (format === "date") {
|
|
4956
6776
|
return jsxRuntime.jsx(DateViewer, { schema: colSchema, prefix, column });
|
|
4957
6777
|
}
|
|
6778
|
+
if (format === "date-time") {
|
|
6779
|
+
return jsxRuntime.jsx(DateTimeViewer, { schema: colSchema, prefix, column });
|
|
6780
|
+
}
|
|
6781
|
+
if (variant === "text-area") {
|
|
6782
|
+
return jsxRuntime.jsx(TextAreaViewer, { schema: colSchema, prefix, column });
|
|
6783
|
+
}
|
|
4958
6784
|
return jsxRuntime.jsx(StringViewer, { schema: colSchema, prefix, column });
|
|
4959
6785
|
}
|
|
4960
6786
|
if (type === "number" || type === "integer") {
|
|
@@ -4980,6 +6806,15 @@ const SchemaViewer = ({ schema, prefix, column, }) => {
|
|
|
4980
6806
|
if (variant === "file-picker") {
|
|
4981
6807
|
return jsxRuntime.jsx(FileViewer, { schema: colSchema, prefix, column });
|
|
4982
6808
|
}
|
|
6809
|
+
if (variant === "enum-picker") {
|
|
6810
|
+
const { items } = schema;
|
|
6811
|
+
const { enum: enumItems } = items;
|
|
6812
|
+
const enumSchema = {
|
|
6813
|
+
type: "string",
|
|
6814
|
+
enum: enumItems,
|
|
6815
|
+
};
|
|
6816
|
+
return (jsxRuntime.jsx(EnumViewer, { isMultiple: true, schema: enumSchema, prefix, column }));
|
|
6817
|
+
}
|
|
4983
6818
|
if (items) {
|
|
4984
6819
|
return jsxRuntime.jsx(ArrayViewer, { schema: colSchema, prefix, column });
|
|
4985
6820
|
}
|
|
@@ -5003,66 +6838,43 @@ const ColumnViewer = ({ column, properties, prefix, }) => {
|
|
|
5003
6838
|
};
|
|
5004
6839
|
|
|
5005
6840
|
const SubmitButton = () => {
|
|
5006
|
-
const { translate, setValidatedData, setIsError, setIsConfirming } = useSchemaContext();
|
|
6841
|
+
const { translate, setValidatedData, setIsError, setIsConfirming, setError, schema, requireConfirmation, onFormSubmit, } = useSchemaContext();
|
|
5007
6842
|
const methods = reactHookForm.useFormContext();
|
|
5008
6843
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5009
6844
|
const onValid = (data) => {
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
6845
|
+
// const { isValid, errors } = validateData(data, schema);
|
|
6846
|
+
// if (!isValid) {
|
|
6847
|
+
// setError({
|
|
6848
|
+
// type: 'validation',
|
|
6849
|
+
// errors,
|
|
6850
|
+
// });
|
|
6851
|
+
// setIsError(true);
|
|
6852
|
+
// return;
|
|
6853
|
+
// }
|
|
6854
|
+
// If validation passes, check if confirmation is required
|
|
6855
|
+
if (requireConfirmation) {
|
|
6856
|
+
// Show confirmation (existing behavior)
|
|
6857
|
+
setValidatedData(data);
|
|
6858
|
+
setIsError(false);
|
|
6859
|
+
setIsConfirming(true);
|
|
6860
|
+
}
|
|
6861
|
+
else {
|
|
6862
|
+
// Skip confirmation and submit directly
|
|
6863
|
+
setValidatedData(data);
|
|
6864
|
+
setIsError(false);
|
|
6865
|
+
onFormSubmit(data);
|
|
6866
|
+
}
|
|
5013
6867
|
};
|
|
5014
6868
|
return (jsxRuntime.jsx(react.Button, { onClick: () => {
|
|
5015
6869
|
methods.handleSubmit(onValid)();
|
|
5016
|
-
}, formNoValidate: true, children: translate.t(
|
|
6870
|
+
}, formNoValidate: true, children: translate.t('submit') }));
|
|
5017
6871
|
};
|
|
5018
6872
|
|
|
5019
6873
|
const FormBody = () => {
|
|
5020
|
-
const { schema,
|
|
6874
|
+
const { schema, order, ignore, include, translate, isSuccess, setIsSuccess, isError, setIsError, isSubmiting, setIsSubmiting, isConfirming, setIsConfirming, validatedData, setValidatedData, error, getUpdatedData, customErrorRenderer, customSuccessRenderer, displayConfig, onFormSubmit, } = useSchemaContext();
|
|
6875
|
+
const { showSubmitButton, showResetButton } = displayConfig;
|
|
5021
6876
|
const methods = reactHookForm.useFormContext();
|
|
5022
6877
|
const { properties } = schema;
|
|
5023
|
-
const onBeforeSubmit = () => {
|
|
5024
|
-
setIsSubmiting(true);
|
|
5025
|
-
};
|
|
5026
|
-
const onAfterSubmit = () => {
|
|
5027
|
-
setIsSubmiting(false);
|
|
5028
|
-
};
|
|
5029
|
-
const onSubmitError = (error) => {
|
|
5030
|
-
setIsError(true);
|
|
5031
|
-
setError(error);
|
|
5032
|
-
};
|
|
5033
|
-
const onSubmitSuccess = () => {
|
|
5034
|
-
setIsSuccess(true);
|
|
5035
|
-
};
|
|
5036
|
-
const defaultOnSubmit = async (promise) => {
|
|
5037
|
-
try {
|
|
5038
|
-
onBeforeSubmit();
|
|
5039
|
-
await promise;
|
|
5040
|
-
onSubmitSuccess();
|
|
5041
|
-
}
|
|
5042
|
-
catch (error) {
|
|
5043
|
-
onSubmitError(error);
|
|
5044
|
-
}
|
|
5045
|
-
finally {
|
|
5046
|
-
onAfterSubmit();
|
|
5047
|
-
}
|
|
5048
|
-
};
|
|
5049
|
-
const defaultSubmitPromise = (data) => {
|
|
5050
|
-
const options = {
|
|
5051
|
-
method: "POST",
|
|
5052
|
-
url: `${requestUrl}`,
|
|
5053
|
-
data: clearEmptyString(data),
|
|
5054
|
-
...requestOptions,
|
|
5055
|
-
};
|
|
5056
|
-
return axios.request(options);
|
|
5057
|
-
};
|
|
5058
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5059
|
-
const onFormSubmit = async (data) => {
|
|
5060
|
-
if (onSubmit === undefined) {
|
|
5061
|
-
await defaultOnSubmit(defaultSubmitPromise(data));
|
|
5062
|
-
return;
|
|
5063
|
-
}
|
|
5064
|
-
await defaultOnSubmit(onSubmit(data));
|
|
5065
|
-
};
|
|
5066
6878
|
const renderColumns = ({ order, keys, ignore, include, }) => {
|
|
5067
6879
|
const included = include.length > 0 ? include : keys;
|
|
5068
6880
|
const not_exist = included.filter((columnA) => !order.some((columnB) => columnA === columnB));
|
|
@@ -5077,38 +6889,42 @@ const FormBody = () => {
|
|
|
5077
6889
|
include,
|
|
5078
6890
|
});
|
|
5079
6891
|
if (isSuccess) {
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
6892
|
+
const resetHandler = async () => {
|
|
6893
|
+
setIsError(false);
|
|
6894
|
+
setIsSubmiting(false);
|
|
6895
|
+
setIsSuccess(false);
|
|
6896
|
+
setIsConfirming(false);
|
|
6897
|
+
setValidatedData(undefined);
|
|
6898
|
+
const data = await getUpdatedData();
|
|
6899
|
+
methods.reset(data);
|
|
6900
|
+
};
|
|
6901
|
+
if (customSuccessRenderer) {
|
|
6902
|
+
return customSuccessRenderer(resetHandler);
|
|
6903
|
+
}
|
|
6904
|
+
return (jsxRuntime.jsxs(react.Flex, { flexFlow: 'column', gap: "2", children: [jsxRuntime.jsxs(react.Alert.Root, { status: "success", children: [jsxRuntime.jsx(react.Alert.Indicator, {}), jsxRuntime.jsx(react.Alert.Content, { children: jsxRuntime.jsx(react.Alert.Title, { children: translate.t('submit_success') }) })] }), jsxRuntime.jsx(react.Flex, { justifyContent: 'end', children: jsxRuntime.jsx(react.Button, { onClick: resetHandler, formNoValidate: true, children: translate.t('submit_again') }) })] }));
|
|
5089
6905
|
}
|
|
5090
6906
|
if (isConfirming) {
|
|
5091
|
-
return (jsxRuntime.jsxs(react.Flex, { flexFlow:
|
|
6907
|
+
return (jsxRuntime.jsxs(react.Flex, { flexFlow: 'column', gap: "2", children: [jsxRuntime.jsx(react.Grid, { gap: 4, gridTemplateColumns: 'repeat(12, 1fr)', gridTemplateRows: 'repeat(12, max-content)', autoFlow: 'row', children: ordered.map((column) => {
|
|
5092
6908
|
return (jsxRuntime.jsx(ColumnViewer
|
|
5093
6909
|
// @ts-expect-error find suitable types
|
|
5094
6910
|
, {
|
|
5095
6911
|
// @ts-expect-error find suitable types
|
|
5096
6912
|
properties: properties, prefix: ``, column }, `form-viewer-${column}`));
|
|
5097
|
-
}) }), jsxRuntime.jsxs(react.Flex, { justifyContent:
|
|
6913
|
+
}) }), jsxRuntime.jsxs(react.Flex, { justifyContent: 'end', gap: '2', children: [jsxRuntime.jsx(react.Button, { onClick: () => {
|
|
5098
6914
|
setIsConfirming(false);
|
|
5099
|
-
}, variant:
|
|
6915
|
+
}, variant: 'subtle', children: translate.t('cancel') }), jsxRuntime.jsx(react.Button, { onClick: () => {
|
|
5100
6916
|
onFormSubmit(validatedData);
|
|
5101
|
-
}, children: translate.t(
|
|
6917
|
+
}, children: translate.t('confirm') })] }), isSubmiting && (jsxRuntime.jsx(react.Box, { pos: "absolute", inset: "0", bg: "bg/80", children: jsxRuntime.jsx(react.Center, { h: "full", children: jsxRuntime.jsx(react.Spinner, { color: "teal.500" }) }) })), isError && customErrorRenderer && customErrorRenderer(error)] }));
|
|
5102
6918
|
}
|
|
5103
|
-
return (jsxRuntime.jsxs(react.Flex, { flexFlow:
|
|
6919
|
+
return (jsxRuntime.jsxs(react.Flex, { flexFlow: 'column', gap: "2", children: [jsxRuntime.jsx(react.Grid, { gap: "4", gridTemplateColumns: 'repeat(12, 1fr)', autoFlow: 'row', children: ordered.map((column) => {
|
|
5104
6920
|
return (jsxRuntime.jsx(ColumnRenderer
|
|
5105
6921
|
// @ts-expect-error find suitable types
|
|
5106
6922
|
, {
|
|
5107
6923
|
// @ts-expect-error find suitable types
|
|
5108
|
-
properties: properties, prefix: ``, column }, `form-input-${column}`));
|
|
5109
|
-
}) }), jsxRuntime.jsxs(react.Flex, { justifyContent:
|
|
6924
|
+
properties: properties, prefix: ``, parentRequired: schema.required, column }, `form-input-${column}`));
|
|
6925
|
+
}) }), jsxRuntime.jsxs(react.Flex, { justifyContent: 'end', gap: "2", children: [showResetButton && (jsxRuntime.jsx(react.Button, { onClick: () => {
|
|
5110
6926
|
methods.reset();
|
|
5111
|
-
}, variant:
|
|
6927
|
+
}, variant: 'subtle', children: translate.t('reset') })), showSubmitButton && jsxRuntime.jsx(SubmitButton, {})] }), isError && customErrorRenderer && customErrorRenderer(error)] }));
|
|
5112
6928
|
};
|
|
5113
6929
|
|
|
5114
6930
|
const FormTitle = () => {
|
|
@@ -5117,15 +6933,19 @@ const FormTitle = () => {
|
|
|
5117
6933
|
};
|
|
5118
6934
|
|
|
5119
6935
|
const DefaultForm = ({ formConfig, }) => {
|
|
5120
|
-
|
|
6936
|
+
const { showTitle } = formConfig.displayConfig ?? {};
|
|
6937
|
+
return (jsxRuntime.jsx(FormRoot, { ...formConfig, children: jsxRuntime.jsxs(react.Grid, { gap: "2", children: [showTitle && jsxRuntime.jsx(FormTitle, {}), jsxRuntime.jsx(FormBody, {})] }) }));
|
|
5121
6938
|
};
|
|
5122
6939
|
|
|
5123
|
-
const useForm = ({ preLoadedValues, keyPrefix }) => {
|
|
6940
|
+
const useForm = ({ preLoadedValues, keyPrefix, namespace, schema, }) => {
|
|
5124
6941
|
const form = reactHookForm.useForm({
|
|
5125
6942
|
values: preLoadedValues,
|
|
6943
|
+
resolver: schema ? ajvResolver(schema) : undefined,
|
|
6944
|
+
mode: 'onBlur',
|
|
6945
|
+
reValidateMode: 'onBlur',
|
|
5126
6946
|
});
|
|
5127
6947
|
const [idMap, setIdMap] = React.useState({});
|
|
5128
|
-
const translate = reactI18next.useTranslation(
|
|
6948
|
+
const translate = reactI18next.useTranslation(namespace || '', { keyPrefix });
|
|
5129
6949
|
return {
|
|
5130
6950
|
form,
|
|
5131
6951
|
idMap,
|
|
@@ -5134,6 +6954,250 @@ const useForm = ({ preLoadedValues, keyPrefix }) => {
|
|
|
5134
6954
|
};
|
|
5135
6955
|
};
|
|
5136
6956
|
|
|
6957
|
+
/**
|
|
6958
|
+
* Type definitions for error message configuration
|
|
6959
|
+
*/
|
|
6960
|
+
/**
|
|
6961
|
+
* Schema-level error message builder
|
|
6962
|
+
*
|
|
6963
|
+
* Builds a complete errorMessage object compatible with ajv-errors plugin.
|
|
6964
|
+
* Supports both i18n translation keys and plain string messages.
|
|
6965
|
+
*
|
|
6966
|
+
* @param config - Error message configuration
|
|
6967
|
+
* @returns Complete errorMessage object for JSON Schema
|
|
6968
|
+
*
|
|
6969
|
+
* @example
|
|
6970
|
+
* ```typescript
|
|
6971
|
+
* // Simple required field errors
|
|
6972
|
+
* const errorMessage = buildErrorMessages({
|
|
6973
|
+
* required: {
|
|
6974
|
+
* username: "Username is required",
|
|
6975
|
+
* email: "user.email.field_required" // i18n key
|
|
6976
|
+
* }
|
|
6977
|
+
* });
|
|
6978
|
+
*
|
|
6979
|
+
* // With validation rules
|
|
6980
|
+
* const errorMessage = buildErrorMessages({
|
|
6981
|
+
* required: {
|
|
6982
|
+
* password: "Password is required"
|
|
6983
|
+
* },
|
|
6984
|
+
* properties: {
|
|
6985
|
+
* password: {
|
|
6986
|
+
* minLength: "Password must be at least 8 characters",
|
|
6987
|
+
* pattern: "Password must contain letters and numbers"
|
|
6988
|
+
* },
|
|
6989
|
+
* age: {
|
|
6990
|
+
* minimum: "Must be 18 or older",
|
|
6991
|
+
* maximum: "Must be under 120"
|
|
6992
|
+
* }
|
|
6993
|
+
* }
|
|
6994
|
+
* });
|
|
6995
|
+
*
|
|
6996
|
+
* // With global fallbacks
|
|
6997
|
+
* const errorMessage = buildErrorMessages({
|
|
6998
|
+
* required: {
|
|
6999
|
+
* email: "Email is required"
|
|
7000
|
+
* },
|
|
7001
|
+
* minLength: "This field is too short", // applies to all fields
|
|
7002
|
+
* minimum: "Value is too small"
|
|
7003
|
+
* });
|
|
7004
|
+
* ```
|
|
7005
|
+
*/
|
|
7006
|
+
const buildErrorMessages = (config) => {
|
|
7007
|
+
const result = {};
|
|
7008
|
+
// Add required field errors
|
|
7009
|
+
if (config.required && Object.keys(config.required).length > 0) {
|
|
7010
|
+
result.required = config.required;
|
|
7011
|
+
}
|
|
7012
|
+
// Add field-specific validation errors
|
|
7013
|
+
if (config.properties && Object.keys(config.properties).length > 0) {
|
|
7014
|
+
result.properties = config.properties;
|
|
7015
|
+
}
|
|
7016
|
+
// Add global fallback error messages
|
|
7017
|
+
const globalKeys = [
|
|
7018
|
+
'minLength',
|
|
7019
|
+
'maxLength',
|
|
7020
|
+
'pattern',
|
|
7021
|
+
'minimum',
|
|
7022
|
+
'maximum',
|
|
7023
|
+
'multipleOf',
|
|
7024
|
+
'format',
|
|
7025
|
+
'type',
|
|
7026
|
+
'enum',
|
|
7027
|
+
];
|
|
7028
|
+
globalKeys.forEach((key) => {
|
|
7029
|
+
if (config[key]) {
|
|
7030
|
+
result[key] = config[key];
|
|
7031
|
+
}
|
|
7032
|
+
});
|
|
7033
|
+
return result;
|
|
7034
|
+
};
|
|
7035
|
+
/**
|
|
7036
|
+
* Converts buildErrorMessages result to ajv-errors compatible format
|
|
7037
|
+
*/
|
|
7038
|
+
const convertToAjvErrorsFormat = (errorMessages) => {
|
|
7039
|
+
const result = {};
|
|
7040
|
+
// Convert required field errors
|
|
7041
|
+
if (errorMessages.required) {
|
|
7042
|
+
result.required = errorMessages.required;
|
|
7043
|
+
}
|
|
7044
|
+
// Convert properties errors to ajv-errors format
|
|
7045
|
+
if (errorMessages.properties) {
|
|
7046
|
+
result.properties = {};
|
|
7047
|
+
Object.keys(errorMessages.properties).forEach((fieldName) => {
|
|
7048
|
+
const fieldErrors = errorMessages.properties[fieldName];
|
|
7049
|
+
result.properties[fieldName] = {};
|
|
7050
|
+
Object.keys(fieldErrors).forEach((keyword) => {
|
|
7051
|
+
result.properties[fieldName][keyword] =
|
|
7052
|
+
fieldErrors[keyword];
|
|
7053
|
+
});
|
|
7054
|
+
});
|
|
7055
|
+
}
|
|
7056
|
+
// Add global fallback errors
|
|
7057
|
+
const globalKeys = [
|
|
7058
|
+
'minLength',
|
|
7059
|
+
'maxLength',
|
|
7060
|
+
'pattern',
|
|
7061
|
+
'minimum',
|
|
7062
|
+
'maximum',
|
|
7063
|
+
'multipleOf',
|
|
7064
|
+
'format',
|
|
7065
|
+
'type',
|
|
7066
|
+
'enum',
|
|
7067
|
+
];
|
|
7068
|
+
globalKeys.forEach((key) => {
|
|
7069
|
+
if (errorMessages[key]) {
|
|
7070
|
+
result[key] = errorMessages[key];
|
|
7071
|
+
}
|
|
7072
|
+
});
|
|
7073
|
+
return result;
|
|
7074
|
+
};
|
|
7075
|
+
/**
|
|
7076
|
+
* Helper function to build required field errors
|
|
7077
|
+
*
|
|
7078
|
+
* Simplifies creating required field error messages, especially useful
|
|
7079
|
+
* for generating i18n translation keys following a pattern.
|
|
7080
|
+
*
|
|
7081
|
+
* @param fields - Array of required field names
|
|
7082
|
+
* @param messageOrGenerator - Either a string template or function to generate messages
|
|
7083
|
+
* @returns Required field error configuration
|
|
7084
|
+
*
|
|
7085
|
+
* @example
|
|
7086
|
+
* ```typescript
|
|
7087
|
+
* // Plain string messages
|
|
7088
|
+
* const required = buildRequiredErrors(
|
|
7089
|
+
* ["username", "email", "password"],
|
|
7090
|
+
* (field) => `${field} is required`
|
|
7091
|
+
* );
|
|
7092
|
+
* // Result: { username: "username is required", email: "email is required", ... }
|
|
7093
|
+
*
|
|
7094
|
+
* // i18n translation keys
|
|
7095
|
+
* const required = buildRequiredErrors(
|
|
7096
|
+
* ["username", "email"],
|
|
7097
|
+
* (field) => `user.${field}.field_required`
|
|
7098
|
+
* );
|
|
7099
|
+
* // Result: { username: "user.username.field_required", email: "user.email.field_required" }
|
|
7100
|
+
*
|
|
7101
|
+
* // Same message for all fields
|
|
7102
|
+
* const required = buildRequiredErrors(
|
|
7103
|
+
* ["username", "email"],
|
|
7104
|
+
* "This field is required"
|
|
7105
|
+
* );
|
|
7106
|
+
* // Result: { username: "This field is required", email: "This field is required" }
|
|
7107
|
+
*
|
|
7108
|
+
* // With keyPrefix for i18n
|
|
7109
|
+
* const required = buildRequiredErrors(
|
|
7110
|
+
* ["username", "email"],
|
|
7111
|
+
* (field) => `${field}.field_required`,
|
|
7112
|
+
* "user"
|
|
7113
|
+
* );
|
|
7114
|
+
* // Result: { username: "user.username.field_required", email: "user.email.field_required" }
|
|
7115
|
+
* ```
|
|
7116
|
+
*/
|
|
7117
|
+
const buildRequiredErrors = (fields, messageOrGenerator, keyPrefix = '') => {
|
|
7118
|
+
const result = {};
|
|
7119
|
+
fields.forEach((field) => {
|
|
7120
|
+
if (typeof messageOrGenerator === 'function') {
|
|
7121
|
+
const message = messageOrGenerator(field);
|
|
7122
|
+
result[field] = keyPrefix ? `${keyPrefix}.${message}` : message;
|
|
7123
|
+
}
|
|
7124
|
+
else {
|
|
7125
|
+
result[field] = messageOrGenerator;
|
|
7126
|
+
}
|
|
7127
|
+
});
|
|
7128
|
+
return result;
|
|
7129
|
+
};
|
|
7130
|
+
/**
|
|
7131
|
+
* Helper function to build field-specific validation errors
|
|
7132
|
+
*
|
|
7133
|
+
* Creates property-specific error messages for multiple fields at once.
|
|
7134
|
+
*
|
|
7135
|
+
* @param config - Maps field names to their validation error configurations
|
|
7136
|
+
* @returns Properties error configuration
|
|
7137
|
+
*
|
|
7138
|
+
* @example
|
|
7139
|
+
* ```typescript
|
|
7140
|
+
* const properties = buildFieldErrors({
|
|
7141
|
+
* username: {
|
|
7142
|
+
* minLength: "Username must be at least 3 characters",
|
|
7143
|
+
* pattern: "Username can only contain letters and numbers"
|
|
7144
|
+
* },
|
|
7145
|
+
* age: {
|
|
7146
|
+
* minimum: "Must be 18 or older",
|
|
7147
|
+
* maximum: "Must be under 120"
|
|
7148
|
+
* },
|
|
7149
|
+
* email: {
|
|
7150
|
+
* format: "Please enter a valid email address"
|
|
7151
|
+
* }
|
|
7152
|
+
* });
|
|
7153
|
+
* ```
|
|
7154
|
+
*/
|
|
7155
|
+
const buildFieldErrors = (config) => {
|
|
7156
|
+
return config;
|
|
7157
|
+
};
|
|
7158
|
+
/**
|
|
7159
|
+
* Helper function to create a complete error message configuration in one call
|
|
7160
|
+
*
|
|
7161
|
+
* Convenient wrapper that combines required and validation errors.
|
|
7162
|
+
*
|
|
7163
|
+
* @param required - Required field error messages
|
|
7164
|
+
* @param properties - Field-specific validation error messages
|
|
7165
|
+
* @param globalFallbacks - Global fallback error messages
|
|
7166
|
+
* @returns Complete error message configuration
|
|
7167
|
+
*
|
|
7168
|
+
* @example
|
|
7169
|
+
* ```typescript
|
|
7170
|
+
* const errorMessage = createErrorMessage(
|
|
7171
|
+
* {
|
|
7172
|
+
* username: "Username is required",
|
|
7173
|
+
* email: "Email is required"
|
|
7174
|
+
* },
|
|
7175
|
+
* {
|
|
7176
|
+
* username: {
|
|
7177
|
+
* minLength: "Username must be at least 3 characters"
|
|
7178
|
+
* },
|
|
7179
|
+
* email: {
|
|
7180
|
+
* format: "Please enter a valid email"
|
|
7181
|
+
* }
|
|
7182
|
+
* },
|
|
7183
|
+
* {
|
|
7184
|
+
* minLength: "This field is too short",
|
|
7185
|
+
* format: "Invalid format"
|
|
7186
|
+
* }
|
|
7187
|
+
* );
|
|
7188
|
+
* ```
|
|
7189
|
+
*/
|
|
7190
|
+
const createErrorMessage = (required, properties, globalFallbacks) => {
|
|
7191
|
+
const config = {
|
|
7192
|
+
required,
|
|
7193
|
+
properties,
|
|
7194
|
+
};
|
|
7195
|
+
if (globalFallbacks) {
|
|
7196
|
+
Object.assign(config, globalFallbacks);
|
|
7197
|
+
}
|
|
7198
|
+
return buildErrorMessages(config);
|
|
7199
|
+
};
|
|
7200
|
+
|
|
5137
7201
|
const getMultiDates = ({ selected, selectedDate, selectedDates, selectable, }) => {
|
|
5138
7202
|
if (!selectable) {
|
|
5139
7203
|
return [...selectedDates];
|
|
@@ -5157,12 +7221,12 @@ exports.DataTableServer = DataTableServer;
|
|
|
5157
7221
|
exports.DefaultCardTitle = DefaultCardTitle;
|
|
5158
7222
|
exports.DefaultForm = DefaultForm;
|
|
5159
7223
|
exports.DefaultTable = DefaultTable;
|
|
7224
|
+
exports.DefaultTableServer = DefaultTableServer;
|
|
5160
7225
|
exports.DensityToggleButton = DensityToggleButton;
|
|
5161
7226
|
exports.EditSortingButton = EditSortingButton;
|
|
5162
7227
|
exports.EmptyState = EmptyState$1;
|
|
5163
7228
|
exports.ErrorAlert = ErrorAlert;
|
|
5164
7229
|
exports.FilterDialog = FilterDialog;
|
|
5165
|
-
exports.FilterOptions = FilterOptions;
|
|
5166
7230
|
exports.FormBody = FormBody;
|
|
5167
7231
|
exports.FormRoot = FormRoot;
|
|
5168
7232
|
exports.FormTitle = FormTitle;
|
|
@@ -5192,6 +7256,11 @@ exports.TableSorter = TableSorter;
|
|
|
5192
7256
|
exports.TableViewer = TableViewer;
|
|
5193
7257
|
exports.TextCell = TextCell;
|
|
5194
7258
|
exports.ViewDialog = ViewDialog;
|
|
7259
|
+
exports.buildErrorMessages = buildErrorMessages;
|
|
7260
|
+
exports.buildFieldErrors = buildFieldErrors;
|
|
7261
|
+
exports.buildRequiredErrors = buildRequiredErrors;
|
|
7262
|
+
exports.convertToAjvErrorsFormat = convertToAjvErrorsFormat;
|
|
7263
|
+
exports.createErrorMessage = createErrorMessage;
|
|
5195
7264
|
exports.getColumns = getColumns;
|
|
5196
7265
|
exports.getMultiDates = getMultiDates;
|
|
5197
7266
|
exports.getRangeDates = getRangeDates;
|