@object-ui/app-shell 11.3.0 → 11.5.0
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 +700 -0
- package/README.md +32 -0
- package/dist/chrome/KeyboardShortcutsDialog.js +2 -1
- package/dist/console/AppContent.js +145 -26
- package/dist/console/ConsoleShell.js +25 -3
- package/dist/console/home/CloudOnboardingNext.d.ts +9 -0
- package/dist/console/home/CloudOnboardingNext.js +14 -4
- package/dist/console/home/HomePage.js +34 -7
- package/dist/console/organizations/CreateWorkspaceDialog.js +33 -3
- package/dist/console/organizations/OrganizationsPage.js +16 -7
- package/dist/context/CommandPaletteProvider.js +2 -1
- package/dist/hooks/useConsoleActionRuntime.js +32 -3
- package/dist/hooks/useObjectActions.js +16 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/dist/layout/AppHeader.js +13 -5
- package/dist/layout/AppSidebar.js +10 -4
- package/dist/observability/sentry.d.ts +5 -0
- package/dist/observability/sentry.js +6 -1
- package/dist/preview/DraftChangesPanel.d.ts +31 -1
- package/dist/preview/DraftChangesPanel.js +146 -18
- package/dist/urlParams.d.ts +68 -0
- package/dist/urlParams.js +76 -0
- package/dist/utils/appRoute.d.ts +15 -0
- package/dist/utils/appRoute.js +22 -0
- package/dist/utils/deriveRelatedLists.d.ts +20 -5
- package/dist/utils/deriveRelatedLists.js +31 -13
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/pageTabsUrlSync.d.ts +32 -0
- package/dist/utils/pageTabsUrlSync.js +43 -0
- package/dist/utils/recordFormNavigation.d.ts +40 -0
- package/dist/utils/recordFormNavigation.js +30 -0
- package/dist/utils/resolveViewId.d.ts +23 -0
- package/dist/utils/resolveViewId.js +37 -0
- package/dist/utils/warnSuppressedListNav.d.ts +10 -0
- package/dist/utils/warnSuppressedListNav.js +40 -0
- package/dist/views/InterfaceListPage.d.ts +1 -0
- package/dist/views/InterfaceListPage.js +7 -5
- package/dist/views/ObjectDataPage.d.ts +29 -0
- package/dist/views/ObjectDataPage.js +227 -0
- package/dist/views/ObjectView.js +65 -13
- package/dist/views/RecordDetailView.js +191 -123
- package/dist/views/RecordFormPage.js +7 -1
- package/dist/views/RelatedRecordActionsBridge.d.ts +33 -0
- package/dist/views/RelatedRecordActionsBridge.js +147 -0
- package/dist/views/metadata-admin/PackagesPage.js +18 -7
- package/dist/views/metadata-admin/PermissionMatrixEditor.d.ts +18 -1
- package/dist/views/metadata-admin/PermissionMatrixEditor.js +73 -14
- package/dist/views/metadata-admin/ResourceEditPage.js +39 -0
- package/dist/views/metadata-admin/i18n.d.ts +12 -21
- package/dist/views/metadata-admin/i18n.js +557 -6
- package/dist/views/metadata-admin/inspectors/AppNavInspector.d.ts +11 -4
- package/dist/views/metadata-admin/inspectors/AppNavInspector.js +141 -7
- package/dist/views/metadata-admin/inspectors/FlowReferenceField.d.ts +14 -0
- package/dist/views/metadata-admin/inspectors/FlowReferenceField.js +76 -5
- package/dist/views/metadata-admin/inspectors/ObjectFieldInspector.js +60 -30
- package/dist/views/metadata-admin/inspectors/flow-node-config.d.ts +8 -1
- package/dist/views/metadata-admin/inspectors/flow-node-config.js +3 -2
- package/dist/views/metadata-admin/inspectors/nav-target.d.ts +52 -0
- package/dist/views/metadata-admin/inspectors/nav-target.js +149 -0
- package/dist/views/metadata-admin/nav-selection.d.ts +20 -0
- package/dist/views/metadata-admin/nav-selection.js +81 -0
- package/dist/views/metadata-admin/permission-slice.d.ts +66 -0
- package/dist/views/metadata-admin/permission-slice.js +70 -0
- package/dist/views/metadata-admin/previews/AppNavCanvas.js +19 -7
- package/dist/views/metadata-admin/previews/AppPreview.js +4 -2
- package/dist/views/metadata-admin/previews/FlowRunsPanel.d.ts +16 -7
- package/dist/views/metadata-admin/previews/FlowRunsPanel.js +18 -2
- package/dist/views/studio-design/BuilderLanding.d.ts +15 -0
- package/dist/views/studio-design/BuilderLanding.js +126 -0
- package/dist/views/studio-design/ObjectFormDesigner.d.ts +33 -0
- package/dist/views/studio-design/ObjectFormDesigner.js +231 -0
- package/dist/views/studio-design/ObjectSettingsPanel.d.ts +30 -0
- package/dist/views/studio-design/ObjectSettingsPanel.js +46 -0
- package/dist/views/studio-design/ObjectValidationsPanel.d.ts +30 -0
- package/dist/views/studio-design/ObjectValidationsPanel.js +80 -0
- package/dist/views/studio-design/PackageIdInput.d.ts +31 -0
- package/dist/views/studio-design/PackageIdInput.js +40 -0
- package/dist/views/studio-design/StudioDesignSurface.d.ts +13 -0
- package/dist/views/studio-design/StudioDesignSurface.js +962 -145
- package/dist/views/studio-design/metadataError.d.ts +23 -0
- package/dist/views/studio-design/metadataError.js +44 -0
- package/dist/views/studio-design/packageSurfaces.d.ts +49 -0
- package/dist/views/studio-design/packageSurfaces.js +34 -0
- package/dist/views/studio-design/packages-io.d.ts +38 -0
- package/dist/views/studio-design/packages-io.js +73 -0
- package/dist/views/studio-design/skeletons.d.ts +16 -0
- package/dist/views/studio-design/skeletons.js +51 -0
- package/package.json +42 -39
package/dist/views/ObjectView.js
CHANGED
|
@@ -15,7 +15,8 @@ import { parseUserFilterParams, applyUserFilterParams } from './userFilterUrlSta
|
|
|
15
15
|
const ObjectChart = lazy(() => import('@object-ui/plugin-charts').then((m) => ({ default: m.ObjectChart })));
|
|
16
16
|
const ImportWizard = lazy(() => import('@object-ui/plugin-grid').then((m) => ({ default: m.ImportWizard })));
|
|
17
17
|
import { ListView } from '@object-ui/plugin-list';
|
|
18
|
-
import { ObjectView as PluginObjectView, ViewTabBar, ManageViewsDialog } from '@object-ui/plugin-view';
|
|
18
|
+
import { ObjectView as PluginObjectView, ViewTabBar, ManageViewsDialog, deriveRecordSurface, overlayWidthFor } from '@object-ui/plugin-view';
|
|
19
|
+
import { RECORD_DRAWER_PARAM } from '../urlParams';
|
|
19
20
|
// Plugin registration is handled by the host app (e.g. apps/console/src/main.tsx
|
|
20
21
|
// uses ComponentRegistry.registerLazy so heavy plugins stay code-split).
|
|
21
22
|
// Do NOT add eager `import '@object-ui/plugin-*'` side-effect imports here.
|
|
@@ -34,6 +35,8 @@ import { ManagedByBadge } from '../components/ManagedByBadge';
|
|
|
34
35
|
import { RecordDetailView } from './RecordDetailView';
|
|
35
36
|
import { resolveCrudAffordances } from '../utils/crudAffordances';
|
|
36
37
|
import { resolveManagedByEmptyState } from '../utils/managedByEmptyState';
|
|
38
|
+
import { resolveViewId } from '../utils/resolveViewId';
|
|
39
|
+
import { warnSuppressedListNav } from '../utils/warnSuppressedListNav';
|
|
37
40
|
import { useObjectActions } from '../hooks/useObjectActions';
|
|
38
41
|
import { useObjectTranslation, useObjectLabel } from '@object-ui/i18n';
|
|
39
42
|
import { usePermissions } from '@object-ui/permissions';
|
|
@@ -190,8 +193,9 @@ function ObjectViewInner({ dataSource, objects, onEdit, externalRefreshKey }) {
|
|
|
190
193
|
'updated_by', 'updatedBy', '_version', '_rev',
|
|
191
194
|
]);
|
|
192
195
|
let defaultColumns = [];
|
|
193
|
-
|
|
194
|
-
|
|
196
|
+
const curated = objectDef?.highlightFields;
|
|
197
|
+
if (Array.isArray(curated) && curated.length > 0) {
|
|
198
|
+
defaultColumns = curated.filter((n) => objectDef.fields?.[n]);
|
|
195
199
|
}
|
|
196
200
|
else if (objectDef?.fields) {
|
|
197
201
|
defaultColumns = Object.entries(objectDef.fields)
|
|
@@ -430,7 +434,7 @@ function ObjectViewInner({ dataSource, objects, onEdit, externalRefreshKey }) {
|
|
|
430
434
|
// Resolve Views from objectDef.listViews (camelCase per @objectstack/spec)
|
|
431
435
|
const views = useMemo(() => {
|
|
432
436
|
// Default column resolution priority:
|
|
433
|
-
// 1. `
|
|
437
|
+
// 1. The `highlightFields` semantic role (ADR-0085).
|
|
434
438
|
// 2. Business fields only — exclude system-managed identifiers/audit
|
|
435
439
|
// columns (id, created_at, updated_at, …) and fields explicitly
|
|
436
440
|
// marked hidden/readonly on the schema. First 5 kept for compactness.
|
|
@@ -440,8 +444,9 @@ function ObjectViewInner({ dataSource, objects, onEdit, externalRefreshKey }) {
|
|
|
440
444
|
'updated_by', 'updatedBy', '_version', '_rev',
|
|
441
445
|
]);
|
|
442
446
|
const resolveDefaultColumns = () => {
|
|
443
|
-
|
|
444
|
-
|
|
447
|
+
const curated = objectDef.highlightFields;
|
|
448
|
+
if (Array.isArray(curated) && curated.length > 0) {
|
|
449
|
+
return curated.filter((n) => objectDef.fields?.[n]);
|
|
445
450
|
}
|
|
446
451
|
if (objectDef.fields) {
|
|
447
452
|
return Object.entries(objectDef.fields)
|
|
@@ -606,7 +611,20 @@ function ObjectViewInner({ dataSource, objects, onEdit, externalRefreshKey }) {
|
|
|
606
611
|
const def = views.find((v) => v.isDefault);
|
|
607
612
|
return def?.id;
|
|
608
613
|
}, [views]);
|
|
609
|
-
|
|
614
|
+
// Canonical view ids are fully qualified (`<object>.<viewKind>`, see
|
|
615
|
+
// MetadataProvider), but nav items emit `viewName` verbatim — usually
|
|
616
|
+
// the short form — and legacy embedded listViews carry bare keys (incl.
|
|
617
|
+
// the `'all'` fallback). Resolve the URL-requested name in both
|
|
618
|
+
// directions, and never swallow a miss silently (#2217).
|
|
619
|
+
const requestedViewId = viewId || searchParams.get('view') || undefined;
|
|
620
|
+
const resolvedViewId = useMemo(() => resolveViewId(requestedViewId, views.map((v) => v.id), objectDef.name), [requestedViewId, views, objectDef.name]);
|
|
621
|
+
useEffect(() => {
|
|
622
|
+
if (requestedViewId && !resolvedViewId) {
|
|
623
|
+
console.warn(`[ObjectView] Requested view "${requestedViewId}" not found on object "${objectDef.name}"; ` +
|
|
624
|
+
`falling back to the default view. Known views: ${views.map((v) => v.id).join(', ')}`);
|
|
625
|
+
}
|
|
626
|
+
}, [requestedViewId, resolvedViewId, objectDef.name, views]);
|
|
627
|
+
const activeViewId = resolvedViewId || defaultViewId || views[0]?.id;
|
|
610
628
|
const baseView = views.find((v) => v.id === activeViewId) || views[0];
|
|
611
629
|
const activeView = viewDraft && viewDraft.id === baseView?.id
|
|
612
630
|
? { ...baseView, ...viewDraft }
|
|
@@ -874,9 +892,27 @@ function ObjectViewInner({ dataSource, objects, onEdit, externalRefreshKey }) {
|
|
|
874
892
|
// RecordDetailView owns its own route — only same-page click navigation
|
|
875
893
|
// is drawer-by-default. Per-view config can still override (e.g. a heavy
|
|
876
894
|
// detail object can set `navigation.mode = 'page'`).
|
|
877
|
-
const detailNavigation = useMemo(() =>
|
|
878
|
-
|
|
879
|
-
|
|
895
|
+
const detailNavigation = useMemo(() => {
|
|
896
|
+
const authored = activeView?.navigation ?? objectDef.navigation;
|
|
897
|
+
if (authored) {
|
|
898
|
+
// Authored config wins. For an overlay, resolve the `size`
|
|
899
|
+
// bucket (or 'auto') to a viewport-clamped width when no explicit
|
|
900
|
+
// width was given — #2578: a pixel width can't be authored blind.
|
|
901
|
+
if (authored.mode === 'page')
|
|
902
|
+
return authored;
|
|
903
|
+
return {
|
|
904
|
+
...authored,
|
|
905
|
+
width: authored.width ?? overlayWidthFor(authored.size, objectDef),
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
// #2578: derive surface + width from FIELD COUNT. Field-heavy → full
|
|
909
|
+
// page (cramped in a drawer); light → a drawer sized to the content
|
|
910
|
+
// and clamped to the viewport. Mobile always pages (in deriveRecordSurface).
|
|
911
|
+
return deriveRecordSurface(objectDef) === 'page'
|
|
912
|
+
? { mode: 'page' }
|
|
913
|
+
: { mode: 'drawer', width: overlayWidthFor('auto', objectDef) };
|
|
914
|
+
}, [activeView?.navigation, objectDef]);
|
|
915
|
+
const drawerRecordId = searchParams.get(RECORD_DRAWER_PARAM);
|
|
880
916
|
/**
|
|
881
917
|
* URL-derived equality filters in the form `?filter[<field>]=<value>`.
|
|
882
918
|
* Used by related-list "View All" buttons to scope the destination list
|
|
@@ -945,7 +981,7 @@ function ObjectViewInner({ dataSource, objects, onEdit, externalRefreshKey }) {
|
|
|
945
981
|
const handleDrawerClose = () => {
|
|
946
982
|
navOverlay.close();
|
|
947
983
|
const newParams = new URLSearchParams(searchParams);
|
|
948
|
-
newParams.delete(
|
|
984
|
+
newParams.delete(RECORD_DRAWER_PARAM);
|
|
949
985
|
setSearchParams(newParams);
|
|
950
986
|
};
|
|
951
987
|
/**
|
|
@@ -969,7 +1005,7 @@ function ObjectViewInner({ dataSource, objects, onEdit, externalRefreshKey }) {
|
|
|
969
1005
|
const id = (record?.id ?? record?._id);
|
|
970
1006
|
if (id != null) {
|
|
971
1007
|
const next = new URLSearchParams(searchParams);
|
|
972
|
-
next.set(
|
|
1008
|
+
next.set(RECORD_DRAWER_PARAM, String(id));
|
|
973
1009
|
setSearchParams(next);
|
|
974
1010
|
return;
|
|
975
1011
|
}
|
|
@@ -1066,6 +1102,11 @@ function ObjectViewInner({ dataSource, objects, onEdit, externalRefreshKey }) {
|
|
|
1066
1102
|
className: 'h-[400px] w-full',
|
|
1067
1103
|
} }) }, key));
|
|
1068
1104
|
}
|
|
1105
|
+
// ADR-0053 wrong-context authoring: userFilters/quickFilters on an
|
|
1106
|
+
// object list view are suppressed below — say so instead of letting
|
|
1107
|
+
// the author stare at a toolbar with nothing where their filter
|
|
1108
|
+
// controls should be (#2219).
|
|
1109
|
+
warnSuppressedListNav(objectDef.name, viewDef.id || viewDef.name || '', viewDef, listSchema);
|
|
1069
1110
|
const fullSchema = {
|
|
1070
1111
|
...listSchema,
|
|
1071
1112
|
// Propagate appearance/view-config properties for live preview
|
|
@@ -1369,11 +1410,22 @@ function ObjectViewInner({ dataSource, objects, onEdit, externalRefreshKey }) {
|
|
|
1369
1410
|
// Import buttons stay visible without pushing the page
|
|
1370
1411
|
// title off-screen.
|
|
1371
1412
|
mobileMaxVisible: 0,
|
|
1372
|
-
} })))] }) }) }), affordances.create && can(objectDef.name, 'create') && (_jsx("button", { type: "button", onClick: actions.create, className: "sm:hidden fixed right-4 bottom-36 z-40 h-12 w-12 rounded-full bg-primary text-primary-foreground shadow-lg active:scale-95 transition-transform inline-flex items-center justify-center", "aria-label": t('console.objectView.new'), "data-testid": "mobile-fab-create", children: _jsx(Plus, { className: "h-5 w-5" }) })), showImport && (_jsx(Suspense, { fallback: null, children: _jsx(ImportWizard, { open: showImport, onOpenChange: setShowImport, objectName: objectDef.name, objectLabel: objectLabel(objectDef), fields: Object.entries(objectDef.fields || {})
|
|
1413
|
+
} })))] }) }) }), affordances.create && can(objectDef.name, 'create') && (_jsx("button", { type: "button", onClick: actions.create, className: "sm:hidden fixed right-4 bottom-36 z-40 h-12 w-12 rounded-full bg-primary text-primary-foreground shadow-lg active:scale-95 transition-transform inline-flex items-center justify-center", "aria-label": t('console.objectView.new'), "data-testid": "mobile-fab-create", children: _jsx(Plus, { className: "h-5 w-5" }) })), showImport && (_jsx(Suspense, { fallback: null, children: _jsx(ImportWizard, { open: showImport, onOpenChange: setShowImport, objectName: objectDef.name, objectLabel: objectLabel(objectDef), fields: Object.entries(objectDef.fields || {})
|
|
1414
|
+
// Only writable fields are importable targets. Computed
|
|
1415
|
+
// types (formula/summary/autonumber) and fields flagged
|
|
1416
|
+
// readonly / write:false are server-rejected, so we omit
|
|
1417
|
+
// them from the mapping step rather than let a user map to
|
|
1418
|
+
// a column the import will silently drop.
|
|
1419
|
+
.filter(([, def]) => !['formula', 'summary', 'autonumber'].includes(def?.type) &&
|
|
1420
|
+
!def?.readonly &&
|
|
1421
|
+
def?.permissions?.write !== false)
|
|
1422
|
+
.map(([name, def]) => ({
|
|
1373
1423
|
name,
|
|
1374
1424
|
label: def?.label || name,
|
|
1375
1425
|
type: def?.type || 'text',
|
|
1376
1426
|
required: !!def?.required,
|
|
1427
|
+
// Enum options seed the downloadable template's example row.
|
|
1428
|
+
...(def?.options ? { options: def.options } : {}),
|
|
1377
1429
|
})), dataSource: dataSource, onComplete: (result) => {
|
|
1378
1430
|
setRefreshKey(k => k + 1);
|
|
1379
1431
|
const ok = result.importedRows;
|