@object-ui/app-shell 4.0.0 → 4.0.1
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/CHANGELOG.md +57 -0
- package/dist/layout/AppHeader.js +8 -1
- package/dist/utils/index.d.ts +7 -4
- package/dist/utils/index.js +44 -12
- package/dist/views/CreateViewDialog.js +10 -1
- package/dist/views/ObjectView.js +32 -6
- package/dist/views/RecordDetailView.js +8 -11
- package/package.json +24 -24
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,62 @@
|
|
|
1
1
|
# @object-ui/app-shell — Changelog
|
|
2
2
|
|
|
3
|
+
## 4.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- @object-ui/types@4.0.1
|
|
8
|
+
- @object-ui/core@4.0.1
|
|
9
|
+
- @object-ui/i18n@4.0.1
|
|
10
|
+
- @object-ui/react@4.0.1
|
|
11
|
+
- @object-ui/components@4.0.1
|
|
12
|
+
- @object-ui/fields@4.0.1
|
|
13
|
+
- @object-ui/layout@4.0.1
|
|
14
|
+
- @object-ui/data-objectstack@4.0.1
|
|
15
|
+
- @object-ui/auth@4.0.1
|
|
16
|
+
- @object-ui/permissions@4.0.1
|
|
17
|
+
- @object-ui/plugin-calendar@4.0.1
|
|
18
|
+
- @object-ui/plugin-charts@4.0.1
|
|
19
|
+
- @object-ui/plugin-chatbot@4.0.1
|
|
20
|
+
- @object-ui/plugin-dashboard@4.0.1
|
|
21
|
+
- @object-ui/plugin-designer@4.0.1
|
|
22
|
+
- @object-ui/plugin-detail@4.0.1
|
|
23
|
+
- @object-ui/plugin-form@4.0.1
|
|
24
|
+
- @object-ui/plugin-grid@4.0.1
|
|
25
|
+
- @object-ui/plugin-kanban@4.0.1
|
|
26
|
+
- @object-ui/plugin-list@4.0.1
|
|
27
|
+
- @object-ui/plugin-report@4.0.1
|
|
28
|
+
- @object-ui/plugin-view@4.0.1
|
|
29
|
+
- @object-ui/collaboration@4.0.1
|
|
30
|
+
|
|
31
|
+
## 4.0.0
|
|
32
|
+
|
|
33
|
+
### Patch Changes
|
|
34
|
+
|
|
35
|
+
- Updated dependencies
|
|
36
|
+
- @object-ui/types@4.0.0
|
|
37
|
+
- @object-ui/auth@4.0.0
|
|
38
|
+
- @object-ui/collaboration@4.0.0
|
|
39
|
+
- @object-ui/components@4.0.0
|
|
40
|
+
- @object-ui/core@4.0.0
|
|
41
|
+
- @object-ui/data-objectstack@4.0.0
|
|
42
|
+
- @object-ui/fields@4.0.0
|
|
43
|
+
- @object-ui/layout@4.0.0
|
|
44
|
+
- @object-ui/permissions@4.0.0
|
|
45
|
+
- @object-ui/plugin-calendar@4.0.0
|
|
46
|
+
- @object-ui/plugin-charts@4.0.0
|
|
47
|
+
- @object-ui/plugin-chatbot@4.0.0
|
|
48
|
+
- @object-ui/plugin-dashboard@4.0.0
|
|
49
|
+
- @object-ui/plugin-designer@4.0.0
|
|
50
|
+
- @object-ui/plugin-detail@4.0.0
|
|
51
|
+
- @object-ui/plugin-form@4.0.0
|
|
52
|
+
- @object-ui/plugin-grid@4.0.0
|
|
53
|
+
- @object-ui/plugin-kanban@4.0.0
|
|
54
|
+
- @object-ui/plugin-list@4.0.0
|
|
55
|
+
- @object-ui/plugin-report@4.0.0
|
|
56
|
+
- @object-ui/plugin-view@4.0.0
|
|
57
|
+
- @object-ui/react@4.0.0
|
|
58
|
+
- @object-ui/i18n@4.0.0
|
|
59
|
+
|
|
3
60
|
## 4.0.0
|
|
4
61
|
|
|
5
62
|
### Patch Changes
|
package/dist/layout/AppHeader.js
CHANGED
|
@@ -181,7 +181,14 @@ export function AppHeader({ variant, appName, objects, connectionState, presence
|
|
|
181
181
|
extraSegments.push({ label: displayTitle || `#${shortId}` });
|
|
182
182
|
}
|
|
183
183
|
else if (pathParts[3] === 'view' && pathParts[4]) {
|
|
184
|
-
|
|
184
|
+
// Prefer the view's metadata label (e.g. "Lead Pipeline") over a
|
|
185
|
+
// humanized slug ("Kanban By Status") so the breadcrumb matches the
|
|
186
|
+
// tab label users clicked.
|
|
187
|
+
const viewName = pathParts[4];
|
|
188
|
+
const definedViews = currentObject.listViews || currentObject.list_views || {};
|
|
189
|
+
const viewDef = definedViews[viewName];
|
|
190
|
+
const viewLabel = (viewDef && (viewDef.label || viewDef.title)) || humanizeSlug(viewName);
|
|
191
|
+
extraSegments.push({ label: viewLabel });
|
|
185
192
|
}
|
|
186
193
|
}
|
|
187
194
|
}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -18,10 +18,13 @@ export declare function resolveI18nLabel(label: string | {
|
|
|
18
18
|
*/
|
|
19
19
|
export declare function capitalizeFirst(str: string): string;
|
|
20
20
|
/**
|
|
21
|
-
* Format a record title using the titleFormat pattern
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
21
|
+
* Format a record title using the titleFormat pattern.
|
|
22
|
+
*
|
|
23
|
+
* Empty placeholders (missing or null/empty fields) are stripped along with
|
|
24
|
+
* any orphan separator they leave behind, so a template like
|
|
25
|
+
* "{full_name} - {company}"
|
|
26
|
+
* evaluated against `{ company: "Acme" }` resolves to `"Acme"` rather than
|
|
27
|
+
* `" - Acme"`. Returns an empty string when no placeholder resolved.
|
|
25
28
|
*/
|
|
26
29
|
export declare function formatRecordTitle(titleFormat: string | undefined, record: any): string;
|
|
27
30
|
/**
|
package/dist/utils/index.js
CHANGED
|
@@ -28,24 +28,47 @@ export function capitalizeFirst(str) {
|
|
|
28
28
|
return str;
|
|
29
29
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
30
30
|
}
|
|
31
|
+
// Sentinel used to mark empty-placeholder positions inside formatRecordTitle
|
|
32
|
+
// so adjacent separators can be stripped in a second pass.
|
|
33
|
+
const EMPTY_TOKEN = '\u0000';
|
|
34
|
+
// Separator characters commonly placed between {fields} in titleFormat patterns
|
|
35
|
+
// (hyphen, em/en dashes, pipes, slashes, middle dot, comma, colon).
|
|
36
|
+
const SEPARATOR_CLASS = '[-\\u2013\\u2014|/·,:]';
|
|
31
37
|
/**
|
|
32
|
-
* Format a record title using the titleFormat pattern
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
38
|
+
* Format a record title using the titleFormat pattern.
|
|
39
|
+
*
|
|
40
|
+
* Empty placeholders (missing or null/empty fields) are stripped along with
|
|
41
|
+
* any orphan separator they leave behind, so a template like
|
|
42
|
+
* "{full_name} - {company}"
|
|
43
|
+
* evaluated against `{ company: "Acme" }` resolves to `"Acme"` rather than
|
|
44
|
+
* `" - Acme"`. Returns an empty string when no placeholder resolved.
|
|
36
45
|
*/
|
|
37
46
|
export function formatRecordTitle(titleFormat, record) {
|
|
38
47
|
if (!titleFormat || !record) {
|
|
39
48
|
return record?.id || record?._id || 'Record';
|
|
40
49
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const value = record[fieldName];
|
|
44
|
-
if (value === null || value === undefined) {
|
|
45
|
-
return
|
|
50
|
+
let anyResolved = false;
|
|
51
|
+
let out = titleFormat.replace(/\{([^{}]+)\}/g, (_match, fieldName) => {
|
|
52
|
+
const value = record[fieldName.trim()];
|
|
53
|
+
if (value === null || value === undefined || value === '') {
|
|
54
|
+
return EMPTY_TOKEN;
|
|
46
55
|
}
|
|
56
|
+
anyResolved = true;
|
|
47
57
|
return String(value);
|
|
48
58
|
});
|
|
59
|
+
if (!anyResolved)
|
|
60
|
+
return '';
|
|
61
|
+
// Drop separators on either side of an empty token, then any leftover
|
|
62
|
+
// tokens, then collapse runs of whitespace.
|
|
63
|
+
const sepBefore = new RegExp(`\\s*${SEPARATOR_CLASS}\\s*${EMPTY_TOKEN}`, 'g');
|
|
64
|
+
const sepAfter = new RegExp(`${EMPTY_TOKEN}\\s*${SEPARATOR_CLASS}\\s*`, 'g');
|
|
65
|
+
out = out
|
|
66
|
+
.replace(sepBefore, '')
|
|
67
|
+
.replace(sepAfter, '')
|
|
68
|
+
.replace(new RegExp(EMPTY_TOKEN, 'g'), '')
|
|
69
|
+
.replace(/\s+/g, ' ')
|
|
70
|
+
.trim();
|
|
71
|
+
return out;
|
|
49
72
|
}
|
|
50
73
|
/**
|
|
51
74
|
* Get display name for a record using titleFormat or fallback
|
|
@@ -55,8 +78,17 @@ export function formatRecordTitle(titleFormat, record) {
|
|
|
55
78
|
*/
|
|
56
79
|
export function getRecordDisplayName(objectDef, record) {
|
|
57
80
|
if (objectDef?.titleFormat) {
|
|
58
|
-
|
|
81
|
+
const formatted = formatRecordTitle(objectDef.titleFormat, record);
|
|
82
|
+
if (formatted)
|
|
83
|
+
return formatted;
|
|
59
84
|
}
|
|
60
|
-
|
|
61
|
-
|
|
85
|
+
return (record?.name ||
|
|
86
|
+
record?.full_name ||
|
|
87
|
+
record?.fullName ||
|
|
88
|
+
record?.title ||
|
|
89
|
+
record?.label ||
|
|
90
|
+
record?.subject ||
|
|
91
|
+
record?.id ||
|
|
92
|
+
record?._id ||
|
|
93
|
+
'Untitled');
|
|
62
94
|
}
|
|
@@ -58,7 +58,16 @@ export function CreateViewDialog({ open, onOpenChange, onCreate, existingLabels,
|
|
|
58
58
|
const types = useMemo(() => (availableTypes && availableTypes.length > 0
|
|
59
59
|
? allTypes.filter(v => availableTypes.includes(v.type))
|
|
60
60
|
: allTypes), [allTypes, availableTypes]);
|
|
61
|
-
|
|
61
|
+
// Stabilise the existing-labels list across renders so we don't churn the
|
|
62
|
+
// `existingSet` memo (and the dependent useEffects) on every parent render.
|
|
63
|
+
// Callers commonly pass `views.map(v => v.label)` inline, which is a fresh
|
|
64
|
+
// array each render — without this normalisation, the name-suggest effect
|
|
65
|
+
// below would re-fire indefinitely and could trigger "Maximum update depth"
|
|
66
|
+
// when the array contents are stable but the reference is not.
|
|
67
|
+
const existingKey = (existingLabels ?? []).join('\u0000');
|
|
68
|
+
const existingSet = useMemo(() => new Set(existingLabels ?? []),
|
|
69
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
70
|
+
[existingKey]);
|
|
62
71
|
const fieldOptions = useMemo(() => (objectDef ? deriveFieldOptions(objectDef) : []), [objectDef]);
|
|
63
72
|
const [selectedType, setSelectedType] = useState(types[0]?.type ?? 'grid');
|
|
64
73
|
const [label, setLabel] = useState('');
|
package/dist/views/ObjectView.js
CHANGED
|
@@ -634,14 +634,17 @@ export function ObjectView({ dataSource, objects, onEdit }) {
|
|
|
634
634
|
const handleSetDefaultView = useCallback(async (vid) => {
|
|
635
635
|
if (!dataSource?.update)
|
|
636
636
|
return;
|
|
637
|
+
if (!isSavedView(vid)) {
|
|
638
|
+
toast.error(t('console.objectView.cannotEditMetaView')
|
|
639
|
+
|| 'System view — duplicate it to mark a default.');
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
637
642
|
try {
|
|
638
643
|
// Clear `isDefault` on all other saved views, then set this one.
|
|
639
644
|
const updates = savedViews
|
|
640
645
|
.filter((sv) => (sv.id || sv._id) !== vid && sv.isDefault)
|
|
641
646
|
.map((sv) => dataSource.update('sys_view', sv.id || sv._id, { isDefault: false }));
|
|
642
|
-
|
|
643
|
-
updates.push(dataSource.update('sys_view', vid, { isDefault: true }));
|
|
644
|
-
}
|
|
647
|
+
updates.push(dataSource.update('sys_view', vid, { isDefault: true }));
|
|
645
648
|
await Promise.all(updates);
|
|
646
649
|
setRefreshKey(k => k + 1);
|
|
647
650
|
}
|
|
@@ -649,7 +652,7 @@ export function ObjectView({ dataSource, objects, onEdit }) {
|
|
|
649
652
|
console.error('[ViewTabBar] Failed to set default view:', err);
|
|
650
653
|
toast.error('Failed to set default view');
|
|
651
654
|
}
|
|
652
|
-
}, [dataSource, savedViews, isSavedView]);
|
|
655
|
+
}, [dataSource, savedViews, isSavedView, t]);
|
|
653
656
|
const handleReorderViews = useCallback(async (orderedIds) => {
|
|
654
657
|
// Persist order for ALL views (incl. metadata) in localStorage so the
|
|
655
658
|
// UI immediately reflects the new ordering, including reorderings
|
|
@@ -677,11 +680,19 @@ export function ObjectView({ dataSource, objects, onEdit }) {
|
|
|
677
680
|
setRefreshKey(k => k + 1);
|
|
678
681
|
}, [dataSource, savedViews, objectName]);
|
|
679
682
|
const handleConfigView = useCallback((vid) => {
|
|
683
|
+
// System (metadata-defined) views are read-only — opening the
|
|
684
|
+
// ViewConfigPanel against one would let the user save changes that
|
|
685
|
+
// never persist. Steer them to "Duplicate view" instead.
|
|
686
|
+
if (!isSavedView(vid)) {
|
|
687
|
+
toast.error(t('console.objectView.cannotEditMetaView')
|
|
688
|
+
|| 'System view — duplicate it to make changes.');
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
680
691
|
if (vid !== activeViewId)
|
|
681
692
|
handleViewChange(vid);
|
|
682
693
|
setViewConfigPanelMode('edit');
|
|
683
694
|
setShowViewConfigPanel(true);
|
|
684
|
-
}, [activeViewId, handleViewChange]);
|
|
695
|
+
}, [activeViewId, handleViewChange, isSavedView, t]);
|
|
685
696
|
const handleAddView = useCallback(() => {
|
|
686
697
|
setShowCreateViewDialog(true);
|
|
687
698
|
}, []);
|
|
@@ -1001,7 +1012,13 @@ export function ObjectView({ dataSource, objects, onEdit }) {
|
|
|
1001
1012
|
center: viewDef.map?.center,
|
|
1002
1013
|
},
|
|
1003
1014
|
gallery: {
|
|
1004
|
-
|
|
1015
|
+
// Spread the full view-defined gallery first so spec
|
|
1016
|
+
// fields (cardSize, visibleFields, coverField, coverFit)
|
|
1017
|
+
// make it through; then layer legacy field aliases that
|
|
1018
|
+
// ObjectGallery still consults.
|
|
1019
|
+
...(viewDef.gallery || {}),
|
|
1020
|
+
imageField: viewDef.gallery?.imageField || viewDef.gallery?.coverField || 'image',
|
|
1021
|
+
coverField: viewDef.gallery?.coverField || viewDef.gallery?.imageField,
|
|
1005
1022
|
titleField: viewDef.gallery?.titleField || objectDef.titleField || 'name',
|
|
1006
1023
|
subtitleField: viewDef.gallery?.subtitleField,
|
|
1007
1024
|
},
|
|
@@ -1072,6 +1089,10 @@ export function ObjectView({ dataSource, objects, onEdit }) {
|
|
|
1072
1089
|
} }))] })] }), views.length >= 1 && (() => {
|
|
1073
1090
|
const viewTabItems = views.map((view) => {
|
|
1074
1091
|
const saved = savedViews.find((sv) => (sv.id || sv._id) === view.id);
|
|
1092
|
+
// System views (loaded from objectDef.listViews / metadata) are
|
|
1093
|
+
// *read-only*. Only sys_view-backed records can be mutated by
|
|
1094
|
+
// the user; admins must duplicate a system view to customize it.
|
|
1095
|
+
const isSystem = !saved;
|
|
1075
1096
|
return {
|
|
1076
1097
|
id: view.id,
|
|
1077
1098
|
label: view.label,
|
|
@@ -1081,6 +1102,11 @@ export function ObjectView({ dataSource, objects, onEdit }) {
|
|
|
1081
1102
|
isDefault: !!(saved?.isDefault ?? view.isDefault),
|
|
1082
1103
|
isPinned: !!(saved?.isPinned ?? view.isPinned),
|
|
1083
1104
|
visibility: saved?.visibility ?? view.visibility,
|
|
1105
|
+
readonly: isSystem,
|
|
1106
|
+
readonlyReason: isSystem
|
|
1107
|
+
? (t('console.objectView.systemViewReadonly')
|
|
1108
|
+
|| 'System view defined in code — duplicate to customize.')
|
|
1109
|
+
: undefined,
|
|
1084
1110
|
};
|
|
1085
1111
|
});
|
|
1086
1112
|
return (_jsxs("div", { className: "border-b px-3 sm:px-4 bg-background overflow-x-auto shrink-0", children: [_jsx(ViewTabBar, { views: viewTabItems, activeViewId: activeViewId, onViewChange: handleViewChange, viewTypeIcons: VIEW_TYPE_ICONS, config: {
|
|
@@ -20,6 +20,7 @@ import { SkeletonDetail } from '../skeletons';
|
|
|
20
20
|
import { ActionConfirmDialog } from './ActionConfirmDialog';
|
|
21
21
|
import { ActionParamDialog } from './ActionParamDialog';
|
|
22
22
|
import { useRecordBreadcrumbTitle } from '../context/NavigationContext';
|
|
23
|
+
import { getRecordDisplayName } from '../utils';
|
|
23
24
|
const FALLBACK_USER = { id: 'current-user', name: 'Demo User' };
|
|
24
25
|
export function RecordDetailView({ dataSource, objects, onEdit }) {
|
|
25
26
|
const { objectName, recordId } = useParams();
|
|
@@ -477,17 +478,13 @@ export function RecordDetailView({ dataSource, objects, onEdit }) {
|
|
|
477
478
|
return (_jsxs("div", { className: "h-full bg-background overflow-hidden flex flex-col relative", children: [_jsx("div", { className: "absolute top-2 sm:top-4 right-2 sm:right-4 z-50 flex items-center gap-2", children: recordViewers.length > 0 && (_jsxs("div", { className: "flex items-center gap-1.5", title: "Users viewing this record", children: [_jsx(Users, { className: "h-3.5 w-3.5 text-muted-foreground" }), _jsx(PresenceAvatars, { users: recordViewers, size: "sm", maxVisible: 4, showStatus: true })] })) }), _jsxs("div", { className: "flex-1 overflow-hidden flex flex-row", children: [_jsx("div", { className: "flex-1 overflow-auto p-3 sm:p-4 lg:p-6 scroll-pb-48", children: _jsx(ActionProvider, { context: { record: {}, objectName, user: currentUser }, onConfirm: confirmHandler, onToast: toastHandler, onNavigate: navigateHandler, onParamCollection: paramCollectionHandler, handlers: { api: apiHandler, flow: flowHandler, script: serverActionHandler, modal: serverActionHandler }, children: _jsx(DetailView, { schema: detailSchema, dataSource: dataSource, objectLabel: objectDef.label, onDataLoaded: (record) => {
|
|
478
479
|
if (!record || typeof record !== 'object')
|
|
479
480
|
return;
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
];
|
|
488
|
-
const next = candidates.find((v) => typeof v === 'string' && v.trim().length > 0);
|
|
489
|
-
if (next && next !== recordTitle)
|
|
490
|
-
setRecordTitle(next);
|
|
481
|
+
// Resolve the same way DetailView's header does, so the
|
|
482
|
+
// breadcrumb matches the on-page title (e.g. "David Kim"
|
|
483
|
+
// instead of "#lead-1778…").
|
|
484
|
+
const resolved = getRecordDisplayName(objectDef, record);
|
|
485
|
+
if (resolved && resolved !== recordTitle && resolved !== 'Untitled') {
|
|
486
|
+
setRecordTitle(resolved);
|
|
487
|
+
}
|
|
491
488
|
}, onEdit: () => {
|
|
492
489
|
onEdit({ id: pureRecordId });
|
|
493
490
|
}, discussionSlot: _jsx(RecordChatterPanel, { config: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@object-ui/app-shell",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Minimal application shell for ObjectUI - framework-agnostic rendering engine",
|
|
@@ -27,34 +27,34 @@
|
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"lucide-react": "^1.14.0",
|
|
29
29
|
"sonner": "^2.0.7",
|
|
30
|
-
"@object-ui/auth": "
|
|
31
|
-
"@object-ui/collaboration": "
|
|
32
|
-
"@object-ui/components": "
|
|
33
|
-
"@object-ui/core": "
|
|
34
|
-
"@object-ui/data-objectstack": "
|
|
35
|
-
"@object-ui/fields": "
|
|
36
|
-
"@object-ui/i18n": "
|
|
37
|
-
"@object-ui/layout": "
|
|
38
|
-
"@object-ui/permissions": "
|
|
39
|
-
"@object-ui/react": "
|
|
40
|
-
"@object-ui/types": "
|
|
30
|
+
"@object-ui/auth": "4.0.1",
|
|
31
|
+
"@object-ui/collaboration": "4.0.1",
|
|
32
|
+
"@object-ui/components": "4.0.1",
|
|
33
|
+
"@object-ui/core": "4.0.1",
|
|
34
|
+
"@object-ui/data-objectstack": "4.0.1",
|
|
35
|
+
"@object-ui/fields": "4.0.1",
|
|
36
|
+
"@object-ui/i18n": "4.0.1",
|
|
37
|
+
"@object-ui/layout": "4.0.1",
|
|
38
|
+
"@object-ui/permissions": "4.0.1",
|
|
39
|
+
"@object-ui/react": "4.0.1",
|
|
40
|
+
"@object-ui/types": "4.0.1"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"react": "^18.0.0 || ^19.0.0",
|
|
44
44
|
"react-dom": "^18.0.0 || ^19.0.0",
|
|
45
45
|
"react-router-dom": "^6.0.0 || ^7.0.0",
|
|
46
|
-
"@object-ui/plugin-calendar": "
|
|
47
|
-
"@object-ui/plugin-charts": "
|
|
48
|
-
"@object-ui/plugin-chatbot": "
|
|
49
|
-
"@object-ui/plugin-dashboard": "
|
|
50
|
-
"@object-ui/plugin-designer": "
|
|
51
|
-
"@object-ui/plugin-detail": "
|
|
52
|
-
"@object-ui/plugin-form": "
|
|
53
|
-
"@object-ui/plugin-grid": "
|
|
54
|
-
"@object-ui/plugin-kanban": "
|
|
55
|
-
"@object-ui/plugin-list": "
|
|
56
|
-
"@object-ui/plugin-report": "
|
|
57
|
-
"@object-ui/plugin-view": "
|
|
46
|
+
"@object-ui/plugin-calendar": "4.0.1",
|
|
47
|
+
"@object-ui/plugin-charts": "4.0.1",
|
|
48
|
+
"@object-ui/plugin-chatbot": "4.0.1",
|
|
49
|
+
"@object-ui/plugin-dashboard": "4.0.1",
|
|
50
|
+
"@object-ui/plugin-designer": "4.0.1",
|
|
51
|
+
"@object-ui/plugin-detail": "4.0.1",
|
|
52
|
+
"@object-ui/plugin-form": "4.0.1",
|
|
53
|
+
"@object-ui/plugin-grid": "4.0.1",
|
|
54
|
+
"@object-ui/plugin-kanban": "4.0.1",
|
|
55
|
+
"@object-ui/plugin-list": "4.0.1",
|
|
56
|
+
"@object-ui/plugin-report": "4.0.1",
|
|
57
|
+
"@object-ui/plugin-view": "4.0.1"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@types/node": "^25.6.0",
|