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