@object-ui/app-shell 3.3.0 → 3.3.2
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 +73 -0
- package/README.md +22 -2
- package/dist/chrome/CommandPalette.d.ts +16 -0
- package/dist/chrome/CommandPalette.js +74 -0
- package/dist/chrome/ConditionalAuthWrapper.d.ts +23 -0
- package/dist/chrome/ConditionalAuthWrapper.js +135 -0
- package/dist/chrome/ConsoleToaster.d.ts +11 -0
- package/dist/chrome/ConsoleToaster.js +28 -0
- package/dist/chrome/ErrorBoundary.d.ts +39 -0
- package/dist/chrome/ErrorBoundary.js +64 -0
- package/dist/chrome/KeyboardShortcutsDialog.d.ts +7 -0
- package/dist/chrome/KeyboardShortcutsDialog.js +63 -0
- package/dist/chrome/LoadingScreen.d.ts +12 -0
- package/dist/chrome/LoadingScreen.js +44 -0
- package/dist/chrome/OnboardingWalkthrough.d.ts +9 -0
- package/dist/chrome/OnboardingWalkthrough.js +11 -0
- package/dist/chrome/ThemeProvider.d.ts +13 -0
- package/dist/chrome/ThemeProvider.js +37 -0
- package/dist/chrome/index.d.ts +8 -0
- package/dist/chrome/index.js +8 -0
- package/dist/console/AppContent.d.ts +26 -0
- package/dist/console/AppContent.js +266 -0
- package/dist/console/ConsoleShell.d.ts +66 -0
- package/dist/console/ConsoleShell.js +97 -0
- package/dist/console/auth/AuthPageLayout.d.ts +9 -0
- package/dist/console/auth/AuthPageLayout.js +4 -0
- package/dist/console/auth/ForgotPasswordPage.d.ts +4 -0
- package/dist/console/auth/ForgotPasswordPage.js +23 -0
- package/dist/console/auth/LoginPage.d.ts +4 -0
- package/dist/console/auth/LoginPage.js +24 -0
- package/dist/console/auth/RegisterPage.d.ts +4 -0
- package/dist/console/auth/RegisterPage.js +29 -0
- package/dist/console/auth/index.d.ts +4 -0
- package/dist/console/auth/index.js +4 -0
- package/dist/console/home/AppCard.d.ts +14 -0
- package/dist/console/home/AppCard.js +36 -0
- package/dist/console/home/HomeLayout.d.ts +15 -0
- package/dist/console/home/HomeLayout.js +20 -0
- package/dist/console/home/HomePage.d.ts +18 -0
- package/dist/console/home/HomePage.js +54 -0
- package/dist/console/home/QuickActions.d.ts +9 -0
- package/dist/console/home/QuickActions.js +53 -0
- package/dist/console/home/RecentApps.d.ts +13 -0
- package/dist/console/home/RecentApps.js +32 -0
- package/dist/console/home/StarredApps.d.ts +13 -0
- package/dist/console/home/StarredApps.js +29 -0
- package/dist/console/home/index.d.ts +11 -0
- package/dist/console/home/index.js +11 -0
- package/dist/console/organizations/CreateWorkspaceDialog.d.ts +16 -0
- package/dist/console/organizations/CreateWorkspaceDialog.js +71 -0
- package/dist/console/organizations/OrganizationsLayout.d.ts +15 -0
- package/dist/console/organizations/OrganizationsLayout.js +20 -0
- package/dist/console/organizations/OrganizationsPage.d.ts +9 -0
- package/dist/console/organizations/OrganizationsPage.js +76 -0
- package/dist/console/organizations/index.d.ts +8 -0
- package/dist/console/organizations/index.js +8 -0
- package/dist/console/organizations/manage/AcceptInvitationPage.d.ts +7 -0
- package/dist/console/organizations/manage/AcceptInvitationPage.js +100 -0
- package/dist/console/organizations/manage/InvitationsPage.d.ts +6 -0
- package/dist/console/organizations/manage/InvitationsPage.js +107 -0
- package/dist/console/organizations/manage/InviteMemberDialog.d.ts +16 -0
- package/dist/console/organizations/manage/InviteMemberDialog.js +83 -0
- package/dist/console/organizations/manage/MembersPage.d.ts +6 -0
- package/dist/console/organizations/manage/MembersPage.js +89 -0
- package/dist/console/organizations/manage/OrganizationLayout.d.ts +8 -0
- package/dist/console/organizations/manage/OrganizationLayout.js +51 -0
- package/dist/console/organizations/manage/SettingsPage.d.ts +6 -0
- package/dist/console/organizations/manage/SettingsPage.js +141 -0
- package/dist/console/organizations/manage/orgContext.d.ts +5 -0
- package/dist/console/organizations/manage/orgContext.js +4 -0
- package/dist/context/FavoritesProvider.d.ts +50 -0
- package/dist/context/FavoritesProvider.js +129 -0
- package/dist/context/NavigationContext.d.ts +46 -0
- package/dist/context/NavigationContext.js +59 -0
- package/dist/context/index.d.ts +3 -0
- package/dist/context/index.js +2 -0
- package/dist/hooks/index.d.ts +7 -0
- package/dist/hooks/index.js +7 -0
- package/dist/hooks/useFavorites.d.ts +13 -0
- package/dist/hooks/useFavorites.js +13 -0
- package/dist/hooks/useMetadataService.d.ts +13 -0
- package/dist/hooks/useMetadataService.js +18 -0
- package/dist/hooks/useNavPins.d.ts +16 -0
- package/dist/hooks/useNavPins.js +72 -0
- package/dist/hooks/useNavigationSync.d.ts +82 -0
- package/dist/hooks/useNavigationSync.js +495 -0
- package/dist/hooks/useObjectActions.d.ts +38 -0
- package/dist/hooks/useObjectActions.js +92 -0
- package/dist/hooks/useRecentItems.d.ts +21 -0
- package/dist/hooks/useRecentItems.js +50 -0
- package/dist/hooks/useResponsiveSidebar.d.ts +8 -0
- package/dist/hooks/useResponsiveSidebar.js +28 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +35 -0
- package/dist/layout/ActivityFeed.d.ts +22 -0
- package/dist/layout/ActivityFeed.js +57 -0
- package/dist/layout/AppHeader.d.ts +33 -0
- package/dist/layout/AppHeader.js +182 -0
- package/dist/layout/AppSidebar.d.ts +14 -0
- package/dist/layout/AppSidebar.js +202 -0
- package/dist/layout/AppSwitcher.d.ts +12 -0
- package/dist/layout/AppSwitcher.js +24 -0
- package/dist/layout/AuthPageLayout.d.ts +9 -0
- package/dist/layout/AuthPageLayout.js +4 -0
- package/dist/layout/ConnectionStatus.d.ts +14 -0
- package/dist/layout/ConnectionStatus.js +36 -0
- package/dist/layout/ConsoleFloatingChatbot.d.ts +10 -0
- package/dist/layout/ConsoleFloatingChatbot.js +27 -0
- package/dist/layout/ConsoleLayout.d.ts +22 -0
- package/dist/layout/ConsoleLayout.js +49 -0
- package/dist/layout/LocaleSwitcher.d.ts +7 -0
- package/dist/layout/LocaleSwitcher.js +27 -0
- package/dist/layout/ModeToggle.d.ts +1 -0
- package/dist/layout/ModeToggle.js +10 -0
- package/dist/layout/UnifiedSidebar.d.ts +27 -0
- package/dist/layout/UnifiedSidebar.js +186 -0
- package/dist/layout/index.d.ts +11 -0
- package/dist/layout/index.js +10 -0
- package/dist/providers/AdapterProvider.d.ts +21 -0
- package/dist/providers/AdapterProvider.js +52 -0
- package/dist/providers/ExpressionProvider.d.ts +50 -0
- package/dist/providers/ExpressionProvider.js +67 -0
- package/dist/providers/MetadataProvider.d.ts +18 -0
- package/dist/providers/MetadataProvider.js +324 -0
- package/dist/services/MetadataService.d.ts +119 -0
- package/dist/services/MetadataService.js +211 -0
- package/dist/skeletons/SkeletonDashboard.d.ts +13 -0
- package/dist/skeletons/SkeletonDashboard.js +12 -0
- package/dist/skeletons/SkeletonDetail.d.ts +15 -0
- package/dist/skeletons/SkeletonDetail.js +12 -0
- package/dist/skeletons/SkeletonGrid.d.ts +15 -0
- package/dist/skeletons/SkeletonGrid.js +12 -0
- package/dist/skeletons/index.d.ts +3 -0
- package/dist/skeletons/index.js +3 -0
- package/dist/utils/getIcon.d.ts +24 -0
- package/dist/utils/getIcon.js +87 -0
- package/dist/utils/index.d.ts +33 -0
- package/dist/utils/index.js +62 -0
- package/dist/utils/metadataConverters.d.ts +78 -0
- package/dist/utils/metadataConverters.js +74 -0
- package/dist/views/ActionConfirmDialog.d.ts +22 -0
- package/dist/views/ActionConfirmDialog.js +22 -0
- package/dist/views/ActionParamDialog.d.ts +23 -0
- package/dist/views/ActionParamDialog.js +67 -0
- package/dist/views/DashboardView.d.ts +9 -0
- package/dist/views/DashboardView.js +357 -0
- package/dist/views/DesignDrawer.d.ts +28 -0
- package/dist/views/DesignDrawer.js +51 -0
- package/dist/views/MetadataInspector.d.ts +40 -0
- package/dist/views/MetadataInspector.js +69 -0
- package/dist/views/ObjectView.d.ts +11 -0
- package/dist/views/ObjectView.js +769 -0
- package/dist/views/PageView.d.ts +6 -0
- package/dist/views/PageView.js +49 -0
- package/dist/views/RecordDetailView.d.ts +14 -0
- package/dist/views/RecordDetailView.js +508 -0
- package/dist/views/ReportView.d.ts +4 -0
- package/dist/views/ReportView.js +285 -0
- package/dist/views/SearchResultsPage.d.ts +9 -0
- package/dist/views/SearchResultsPage.js +107 -0
- package/dist/views/ViewConfigPanel.d.ts +56 -0
- package/dist/views/ViewConfigPanel.js +91 -0
- package/dist/views/index.d.ts +11 -0
- package/dist/views/index.js +11 -0
- package/package.json +59 -11
- package/.turbo/turbo-build.log +0 -4
- package/src/components/AppShell.tsx +0 -31
- package/src/components/DashboardRenderer.tsx +0 -36
- package/src/components/FormRenderer.tsx +0 -71
- package/src/components/ObjectRenderer.tsx +0 -122
- package/src/components/PageRenderer.tsx +0 -28
- package/src/index.ts +0 -20
- package/src/types.ts +0 -78
- package/tsconfig.json +0 -20
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* SkeletonDetail
|
|
4
|
+
*
|
|
5
|
+
* Skeleton loading placeholder for record detail views.
|
|
6
|
+
* Renders animated pulse fields that mimic a detail form layout.
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { Skeleton } from '@object-ui/components';
|
|
10
|
+
export function SkeletonDetail({ fields = 8, columns = 2 }) {
|
|
11
|
+
return (_jsxs("div", { className: "w-full space-y-6 p-4 sm:p-6", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Skeleton, { className: "h-8 w-8 rounded" }), _jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-6 w-48" }), _jsx(Skeleton, { className: "h-4 w-32" })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Skeleton, { className: "h-9 w-20" }), _jsx(Skeleton, { className: "h-9 w-20" })] })] }), _jsx(Skeleton, { className: "h-5 w-24" }), _jsx("div", { className: `grid grid-cols-1 ${columns === 2 ? 'md:grid-cols-2' : ''} gap-4`, children: Array.from({ length: fields }).map((_, i) => (_jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-4 w-24" }), _jsx(Skeleton, { className: "h-9 w-full" })] }, `field-${i}`))) }), _jsxs("div", { className: "space-y-3 pt-4 border-t", children: [_jsx(Skeleton, { className: "h-5 w-32" }), _jsx("div", { className: "space-y-2", children: Array.from({ length: 3 }).map((_, i) => (_jsxs("div", { className: "flex items-center gap-3 py-2", children: [_jsx(Skeleton, { className: "h-8 w-8 rounded-full" }), _jsxs("div", { className: "flex-1 space-y-1", children: [_jsx(Skeleton, { className: "h-4 w-40" }), _jsx(Skeleton, { className: "h-3 w-24" })] })] }, `related-${i}`))) })] })] }));
|
|
12
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SkeletonGrid
|
|
3
|
+
*
|
|
4
|
+
* Skeleton loading placeholder for grid/table views.
|
|
5
|
+
* Renders animated pulse rows that mimic a data table layout.
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
interface SkeletonGridProps {
|
|
9
|
+
/** Number of skeleton rows to render */
|
|
10
|
+
rows?: number;
|
|
11
|
+
/** Number of columns to render */
|
|
12
|
+
columns?: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function SkeletonGrid({ rows, columns }: SkeletonGridProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* SkeletonGrid
|
|
4
|
+
*
|
|
5
|
+
* Skeleton loading placeholder for grid/table views.
|
|
6
|
+
* Renders animated pulse rows that mimic a data table layout.
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { Skeleton } from '@object-ui/components';
|
|
10
|
+
export function SkeletonGrid({ rows = 8, columns = 5 }) {
|
|
11
|
+
return (_jsxs("div", { className: "w-full space-y-4 p-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Skeleton, { className: "h-9 w-48" }), _jsx(Skeleton, { className: "h-9 w-24" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Skeleton, { className: "h-9 w-9" }), _jsx(Skeleton, { className: "h-9 w-9" })] })] }), _jsxs("div", { className: "rounded-md border", children: [_jsx("div", { className: "flex items-center gap-4 border-b px-4 py-3 bg-muted/30", children: Array.from({ length: columns }).map((_, i) => (_jsx(Skeleton, { className: "h-4 flex-1" }, `header-${i}`))) }), Array.from({ length: rows }).map((_, rowIdx) => (_jsx("div", { className: "flex items-center gap-4 border-b last:border-b-0 px-4 py-3", children: Array.from({ length: columns }).map((_, colIdx) => (_jsx(Skeleton, { className: `h-4 flex-1 ${colIdx === 0 ? 'max-w-[200px]' : ''}` }, `cell-${rowIdx}-${colIdx}`))) }, `row-${rowIdx}`)))] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Skeleton, { className: "h-4 w-40" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Skeleton, { className: "h-8 w-8" }), _jsx(Skeleton, { className: "h-8 w-8" }), _jsx(Skeleton, { className: "h-8 w-8" })] })] })] }));
|
|
12
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Icon utilities
|
|
3
|
+
*
|
|
4
|
+
* Helpers for resolving Lucide icons by name.
|
|
5
|
+
*
|
|
6
|
+
* Implementation: instead of statically importing every icon (~1500
|
|
7
|
+
* components, ~568 KB raw / 140 KB gz), we wrap lucide-react's built-in
|
|
8
|
+
* `DynamicIcon` so each icon is fetched as its own tiny chunk on first use.
|
|
9
|
+
*
|
|
10
|
+
* The exported `getIcon(name)` API stays synchronous and returns a React
|
|
11
|
+
* component, preserving call sites that do `const Icon = getIcon(name); <Icon />`.
|
|
12
|
+
*/
|
|
13
|
+
import React from 'react';
|
|
14
|
+
/**
|
|
15
|
+
* Resolve a Lucide icon by name (kebab-case or PascalCase).
|
|
16
|
+
*
|
|
17
|
+
* Returns a React component that lazy-loads the underlying SVG icon on
|
|
18
|
+
* mount. Falls back to the `Database` icon (statically imported) when no
|
|
19
|
+
* `name` is given.
|
|
20
|
+
*
|
|
21
|
+
* The returned component is memoised per `name` so repeated calls with the
|
|
22
|
+
* same name yield the same component reference (stable for React.memo).
|
|
23
|
+
*/
|
|
24
|
+
export declare function getIcon(name?: string): React.ElementType;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Icon utilities
|
|
3
|
+
*
|
|
4
|
+
* Helpers for resolving Lucide icons by name.
|
|
5
|
+
*
|
|
6
|
+
* Implementation: instead of statically importing every icon (~1500
|
|
7
|
+
* components, ~568 KB raw / 140 KB gz), we wrap lucide-react's built-in
|
|
8
|
+
* `DynamicIcon` so each icon is fetched as its own tiny chunk on first use.
|
|
9
|
+
*
|
|
10
|
+
* The exported `getIcon(name)` API stays synchronous and returns a React
|
|
11
|
+
* component, preserving call sites that do `const Icon = getIcon(name); <Icon />`.
|
|
12
|
+
*/
|
|
13
|
+
import React from 'react';
|
|
14
|
+
import { Database } from 'lucide-react';
|
|
15
|
+
// @ts-expect-error - lucide-react has no `exports` field; subpath types live alongside dynamic.mjs
|
|
16
|
+
import { DynamicIcon } from 'lucide-react/dynamic.mjs';
|
|
17
|
+
/** Convert PascalCase / camelCase / mixed names to kebab-case for DynamicIcon. */
|
|
18
|
+
function toKebab(name) {
|
|
19
|
+
if (name.includes('-'))
|
|
20
|
+
return name.toLowerCase();
|
|
21
|
+
return name
|
|
22
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
23
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
|
|
24
|
+
.toLowerCase();
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Aliases for icon names that exist in other icon sets (FontAwesome, Material,
|
|
28
|
+
* Bootstrap…) but use a different name in Lucide. Mapped to the closest
|
|
29
|
+
* Lucide equivalent so existing metadata authored against FA stays portable.
|
|
30
|
+
*/
|
|
31
|
+
const ICON_ALIASES = {
|
|
32
|
+
bullseye: 'target',
|
|
33
|
+
bullhorn: 'megaphone',
|
|
34
|
+
tachometer: 'gauge',
|
|
35
|
+
'tachometer-alt': 'gauge',
|
|
36
|
+
dashboard: 'layout-dashboard',
|
|
37
|
+
'life-ring': 'life-buoy',
|
|
38
|
+
tasks: 'list-checks',
|
|
39
|
+
'file-invoice': 'receipt',
|
|
40
|
+
'file-signature': 'file-pen-line',
|
|
41
|
+
'file-pdf': 'file-text',
|
|
42
|
+
'check-square': 'square-check',
|
|
43
|
+
'arrow-up-right-from-square': 'external-link',
|
|
44
|
+
'sign-out-alt': 'log-out',
|
|
45
|
+
'sign-in-alt': 'log-in',
|
|
46
|
+
'cog': 'settings',
|
|
47
|
+
'gears': 'settings',
|
|
48
|
+
'envelope': 'mail',
|
|
49
|
+
'phone-alt': 'phone',
|
|
50
|
+
'map-marker-alt': 'map-pin',
|
|
51
|
+
'edit': 'pencil',
|
|
52
|
+
'trash-alt': 'trash-2',
|
|
53
|
+
'plus-circle': 'circle-plus',
|
|
54
|
+
'minus-circle': 'circle-minus',
|
|
55
|
+
'times-circle': 'circle-x',
|
|
56
|
+
'check-circle': 'circle-check',
|
|
57
|
+
'info-circle': 'info',
|
|
58
|
+
'question-circle': 'circle-help',
|
|
59
|
+
'exclamation-triangle': 'triangle-alert',
|
|
60
|
+
};
|
|
61
|
+
const cache = new Map();
|
|
62
|
+
/**
|
|
63
|
+
* Resolve a Lucide icon by name (kebab-case or PascalCase).
|
|
64
|
+
*
|
|
65
|
+
* Returns a React component that lazy-loads the underlying SVG icon on
|
|
66
|
+
* mount. Falls back to the `Database` icon (statically imported) when no
|
|
67
|
+
* `name` is given.
|
|
68
|
+
*
|
|
69
|
+
* The returned component is memoised per `name` so repeated calls with the
|
|
70
|
+
* same name yield the same component reference (stable for React.memo).
|
|
71
|
+
*/
|
|
72
|
+
export function getIcon(name) {
|
|
73
|
+
if (!name)
|
|
74
|
+
return Database;
|
|
75
|
+
const cached = cache.get(name);
|
|
76
|
+
if (cached)
|
|
77
|
+
return cached;
|
|
78
|
+
const kebab = ICON_ALIASES[toKebab(name)] ?? toKebab(name);
|
|
79
|
+
const Wrapped = (props) => React.createElement(DynamicIcon, {
|
|
80
|
+
name: kebab,
|
|
81
|
+
fallback: Database,
|
|
82
|
+
...props,
|
|
83
|
+
});
|
|
84
|
+
Wrapped.displayName = `LucideIcon(${name})`;
|
|
85
|
+
cache.set(name, Wrapped);
|
|
86
|
+
return Wrapped;
|
|
87
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for ObjectStack Console
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Resolves an I18nLabel to a plain string.
|
|
6
|
+
* I18nLabel can be either a string or an object { key, defaultValue?, params? }.
|
|
7
|
+
* When it's an object and a `t` function is provided, it resolves the key
|
|
8
|
+
* through the i18n translation system. Otherwise returns defaultValue or key.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveI18nLabel(label: string | {
|
|
11
|
+
key: string;
|
|
12
|
+
defaultValue?: string;
|
|
13
|
+
params?: Record<string, any>;
|
|
14
|
+
} | undefined, t?: (key: string, options?: any) => string): string | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Capitalize the first letter of a string.
|
|
17
|
+
* Preferred over CSS `capitalize` for i18n compatibility.
|
|
18
|
+
*/
|
|
19
|
+
export declare function capitalizeFirst(str: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Format a record title using the titleFormat pattern
|
|
22
|
+
* @param titleFormat Pattern like "{name} - {email}" or "{firstName} {lastName}"
|
|
23
|
+
* @param record The record data object
|
|
24
|
+
* @returns Formatted title string
|
|
25
|
+
*/
|
|
26
|
+
export declare function formatRecordTitle(titleFormat: string | undefined, record: any): string;
|
|
27
|
+
/**
|
|
28
|
+
* Get display name for a record using titleFormat or fallback
|
|
29
|
+
* @param objectDef Object definition with optional titleFormat
|
|
30
|
+
* @param record The record data
|
|
31
|
+
* @returns Display name for the record
|
|
32
|
+
*/
|
|
33
|
+
export declare function getRecordDisplayName(objectDef: any, record: any): string;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for ObjectStack Console
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Resolves an I18nLabel to a plain string.
|
|
6
|
+
* I18nLabel can be either a string or an object { key, defaultValue?, params? }.
|
|
7
|
+
* When it's an object and a `t` function is provided, it resolves the key
|
|
8
|
+
* through the i18n translation system. Otherwise returns defaultValue or key.
|
|
9
|
+
*/
|
|
10
|
+
export function resolveI18nLabel(label, t) {
|
|
11
|
+
if (label === undefined || label === null)
|
|
12
|
+
return undefined;
|
|
13
|
+
if (typeof label === 'string')
|
|
14
|
+
return label;
|
|
15
|
+
if (t) {
|
|
16
|
+
const result = t(label.key, { defaultValue: label.defaultValue, ...label.params });
|
|
17
|
+
if (result && result !== label.key)
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
return label.defaultValue || label.key;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Capitalize the first letter of a string.
|
|
24
|
+
* Preferred over CSS `capitalize` for i18n compatibility.
|
|
25
|
+
*/
|
|
26
|
+
export function capitalizeFirst(str) {
|
|
27
|
+
if (!str)
|
|
28
|
+
return str;
|
|
29
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Format a record title using the titleFormat pattern
|
|
33
|
+
* @param titleFormat Pattern like "{name} - {email}" or "{firstName} {lastName}"
|
|
34
|
+
* @param record The record data object
|
|
35
|
+
* @returns Formatted title string
|
|
36
|
+
*/
|
|
37
|
+
export function formatRecordTitle(titleFormat, record) {
|
|
38
|
+
if (!titleFormat || !record) {
|
|
39
|
+
return record?.id || record?._id || 'Record';
|
|
40
|
+
}
|
|
41
|
+
// Replace {fieldName} patterns with actual values
|
|
42
|
+
return titleFormat.replace(/\{(\w+)\}/g, (_match, fieldName) => {
|
|
43
|
+
const value = record[fieldName];
|
|
44
|
+
if (value === null || value === undefined) {
|
|
45
|
+
return '';
|
|
46
|
+
}
|
|
47
|
+
return String(value);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Get display name for a record using titleFormat or fallback
|
|
52
|
+
* @param objectDef Object definition with optional titleFormat
|
|
53
|
+
* @param record The record data
|
|
54
|
+
* @returns Display name for the record
|
|
55
|
+
*/
|
|
56
|
+
export function getRecordDisplayName(objectDef, record) {
|
|
57
|
+
if (objectDef?.titleFormat) {
|
|
58
|
+
return formatRecordTitle(objectDef.titleFormat, record);
|
|
59
|
+
}
|
|
60
|
+
// Fallback: Try common name fields
|
|
61
|
+
return record?.name || record?.title || record?.label || record?.id || record?._id || 'Untitled';
|
|
62
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metadata Converters
|
|
3
|
+
*
|
|
4
|
+
* Shared conversion functions for transforming raw metadata API objects
|
|
5
|
+
* (from the ObjectStack spec) to the UI types used by ObjectManager and
|
|
6
|
+
* FieldDesigner components.
|
|
7
|
+
*
|
|
8
|
+
* Extracted from ObjectManagerPage to enable reuse across pages.
|
|
9
|
+
*
|
|
10
|
+
* @module utils/metadataConverters
|
|
11
|
+
*/
|
|
12
|
+
import type { ObjectDefinition, DesignerFieldDefinition } from '@object-ui/types';
|
|
13
|
+
/** Loose shape of a metadata object definition from the ObjectStack API. */
|
|
14
|
+
export interface MetadataObject {
|
|
15
|
+
name?: string;
|
|
16
|
+
label?: string | {
|
|
17
|
+
defaultValue?: string;
|
|
18
|
+
key?: string;
|
|
19
|
+
};
|
|
20
|
+
pluralLabel?: string;
|
|
21
|
+
plural_label?: string;
|
|
22
|
+
description?: string | {
|
|
23
|
+
defaultValue?: string;
|
|
24
|
+
};
|
|
25
|
+
icon?: string;
|
|
26
|
+
enabled?: boolean;
|
|
27
|
+
fields?: MetadataField[] | Record<string, MetadataField>;
|
|
28
|
+
relationships?: Array<{
|
|
29
|
+
object?: string;
|
|
30
|
+
relatedObject?: string;
|
|
31
|
+
type?: string;
|
|
32
|
+
label?: string;
|
|
33
|
+
name?: string;
|
|
34
|
+
foreign_key?: string;
|
|
35
|
+
foreignKey?: string;
|
|
36
|
+
}>;
|
|
37
|
+
}
|
|
38
|
+
/** Loose shape of a metadata field definition from the ObjectStack API. */
|
|
39
|
+
export interface MetadataField {
|
|
40
|
+
name?: string;
|
|
41
|
+
label?: string | {
|
|
42
|
+
defaultValue?: string;
|
|
43
|
+
key?: string;
|
|
44
|
+
};
|
|
45
|
+
type?: string;
|
|
46
|
+
group?: string;
|
|
47
|
+
description?: string;
|
|
48
|
+
help?: string;
|
|
49
|
+
required?: boolean;
|
|
50
|
+
unique?: boolean;
|
|
51
|
+
readonly?: boolean;
|
|
52
|
+
hidden?: boolean;
|
|
53
|
+
defaultValue?: string;
|
|
54
|
+
default_value?: string;
|
|
55
|
+
placeholder?: string;
|
|
56
|
+
options?: Array<string | {
|
|
57
|
+
label?: string;
|
|
58
|
+
value: string;
|
|
59
|
+
color?: string;
|
|
60
|
+
}>;
|
|
61
|
+
externalId?: boolean;
|
|
62
|
+
trackHistory?: boolean;
|
|
63
|
+
track_history?: boolean;
|
|
64
|
+
indexed?: boolean;
|
|
65
|
+
reference_to?: string;
|
|
66
|
+
referenceTo?: string;
|
|
67
|
+
formula?: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Convert a metadata object definition (from the API/spec) to the ObjectDefinition
|
|
71
|
+
* type used by the ObjectManager component.
|
|
72
|
+
*/
|
|
73
|
+
export declare function toObjectDefinition(obj: MetadataObject, index: number): ObjectDefinition;
|
|
74
|
+
/**
|
|
75
|
+
* Convert a metadata field definition to the DesignerFieldDefinition
|
|
76
|
+
* type used by the FieldDesigner component.
|
|
77
|
+
*/
|
|
78
|
+
export declare function toFieldDefinition(field: MetadataField, index: number): DesignerFieldDefinition;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metadata Converters
|
|
3
|
+
*
|
|
4
|
+
* Shared conversion functions for transforming raw metadata API objects
|
|
5
|
+
* (from the ObjectStack spec) to the UI types used by ObjectManager and
|
|
6
|
+
* FieldDesigner components.
|
|
7
|
+
*
|
|
8
|
+
* Extracted from ObjectManagerPage to enable reuse across pages.
|
|
9
|
+
*
|
|
10
|
+
* @module utils/metadataConverters
|
|
11
|
+
*/
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Converters
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
/**
|
|
16
|
+
* Convert a metadata object definition (from the API/spec) to the ObjectDefinition
|
|
17
|
+
* type used by the ObjectManager component.
|
|
18
|
+
*/
|
|
19
|
+
export function toObjectDefinition(obj, index) {
|
|
20
|
+
const fields = Array.isArray(obj.fields) ? obj.fields : Object.values(obj.fields || {});
|
|
21
|
+
return {
|
|
22
|
+
id: obj.name || `obj_${index}`,
|
|
23
|
+
name: obj.name || '',
|
|
24
|
+
label: typeof obj.label === 'object' ? obj.label.defaultValue || obj.label.key || '' : (obj.label || obj.name || ''),
|
|
25
|
+
pluralLabel: obj.pluralLabel || obj.plural_label || undefined,
|
|
26
|
+
description: typeof obj.description === 'object' ? obj.description.defaultValue : (obj.description || undefined),
|
|
27
|
+
icon: obj.icon || undefined,
|
|
28
|
+
group: obj.name?.startsWith('sys_') ? 'System Objects' : 'Custom Objects',
|
|
29
|
+
sortOrder: index,
|
|
30
|
+
isSystem: obj.name?.startsWith('sys_') || false,
|
|
31
|
+
enabled: obj.enabled !== false,
|
|
32
|
+
fieldCount: fields.length,
|
|
33
|
+
relationships: Array.isArray(obj.relationships)
|
|
34
|
+
? obj.relationships.map((r) => ({
|
|
35
|
+
relatedObject: r.object || r.relatedObject || '',
|
|
36
|
+
type: (r.type || 'one-to-many'),
|
|
37
|
+
label: r.label || r.name || undefined,
|
|
38
|
+
foreignKey: r.foreign_key || r.foreignKey || undefined,
|
|
39
|
+
}))
|
|
40
|
+
: undefined,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Convert a metadata field definition to the DesignerFieldDefinition
|
|
45
|
+
* type used by the FieldDesigner component.
|
|
46
|
+
*/
|
|
47
|
+
export function toFieldDefinition(field, index) {
|
|
48
|
+
return {
|
|
49
|
+
id: field.name || `fld_${index}`,
|
|
50
|
+
name: field.name || '',
|
|
51
|
+
label: typeof field.label === 'object' ? field.label.defaultValue || field.label.key || '' : (field.label || field.name || ''),
|
|
52
|
+
type: (field.type || 'text'),
|
|
53
|
+
group: field.group || undefined,
|
|
54
|
+
sortOrder: index,
|
|
55
|
+
description: field.description || field.help || undefined,
|
|
56
|
+
required: field.required || false,
|
|
57
|
+
unique: field.unique || false,
|
|
58
|
+
readonly: field.readonly || false,
|
|
59
|
+
hidden: field.hidden || false,
|
|
60
|
+
defaultValue: field.defaultValue || field.default_value || undefined,
|
|
61
|
+
placeholder: field.placeholder || undefined,
|
|
62
|
+
options: Array.isArray(field.options)
|
|
63
|
+
? field.options.map((opt) => typeof opt === 'string'
|
|
64
|
+
? { label: opt, value: opt }
|
|
65
|
+
: { label: opt.label || opt.value, value: opt.value, color: opt.color })
|
|
66
|
+
: undefined,
|
|
67
|
+
isSystem: field.readonly === true && (field.name === 'id' || field.name === 'createdAt' || field.name === 'updatedAt'),
|
|
68
|
+
externalId: field.externalId || false,
|
|
69
|
+
trackHistory: field.trackHistory || field.track_history || false,
|
|
70
|
+
indexed: field.indexed || false,
|
|
71
|
+
referenceTo: field.reference_to || field.referenceTo || undefined,
|
|
72
|
+
formula: field.formula || undefined,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ActionConfirmDialog — Promise-based confirmation dialog for action execution.
|
|
3
|
+
*
|
|
4
|
+
* Uses Shadcn AlertDialog to replace window.confirm with a styled, accessible
|
|
5
|
+
* confirmation dialog. Renders only when state.open is true.
|
|
6
|
+
*/
|
|
7
|
+
export interface ConfirmDialogState {
|
|
8
|
+
open: boolean;
|
|
9
|
+
message: string;
|
|
10
|
+
options?: {
|
|
11
|
+
title?: string;
|
|
12
|
+
confirmText?: string;
|
|
13
|
+
cancelText?: string;
|
|
14
|
+
};
|
|
15
|
+
resolve?: (value: boolean) => void;
|
|
16
|
+
}
|
|
17
|
+
interface ActionConfirmDialogProps {
|
|
18
|
+
state: ConfirmDialogState;
|
|
19
|
+
onOpenChange: (open: boolean) => void;
|
|
20
|
+
}
|
|
21
|
+
export declare function ActionConfirmDialog({ state, onOpenChange }: ActionConfirmDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* ActionConfirmDialog — Promise-based confirmation dialog for action execution.
|
|
4
|
+
*
|
|
5
|
+
* Uses Shadcn AlertDialog to replace window.confirm with a styled, accessible
|
|
6
|
+
* confirmation dialog. Renders only when state.open is true.
|
|
7
|
+
*/
|
|
8
|
+
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@object-ui/components';
|
|
9
|
+
export function ActionConfirmDialog({ state, onOpenChange }) {
|
|
10
|
+
const handleConfirm = () => {
|
|
11
|
+
state.resolve?.(true);
|
|
12
|
+
onOpenChange(false);
|
|
13
|
+
};
|
|
14
|
+
const handleCancel = () => {
|
|
15
|
+
state.resolve?.(false);
|
|
16
|
+
onOpenChange(false);
|
|
17
|
+
};
|
|
18
|
+
return (_jsx(AlertDialog, { open: state.open, onOpenChange: (open) => {
|
|
19
|
+
if (!open)
|
|
20
|
+
handleCancel();
|
|
21
|
+
}, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: state.options?.title || 'Confirm Action' }), _jsx(AlertDialogDescription, { children: state.message })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { onClick: handleCancel, children: state.options?.cancelText || 'Cancel' }), _jsx(AlertDialogAction, { onClick: handleConfirm, children: state.options?.confirmText || 'Continue' })] })] }) }));
|
|
22
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ActionParamDialog — Collects user input for action parameters before execution.
|
|
3
|
+
*
|
|
4
|
+
* Dynamically renders form fields from ActionParamDef[] definitions:
|
|
5
|
+
* - type: 'select' → Shadcn Select component
|
|
6
|
+
* - type: 'text' → Shadcn Input component
|
|
7
|
+
* - type: 'textarea' → Shadcn Textarea component
|
|
8
|
+
* - other types → Shadcn Input with appropriate HTML type
|
|
9
|
+
*
|
|
10
|
+
* Returns collected param values or null on cancel.
|
|
11
|
+
*/
|
|
12
|
+
import type { ActionParamDef } from '@object-ui/core';
|
|
13
|
+
export interface ParamDialogState {
|
|
14
|
+
open: boolean;
|
|
15
|
+
params: ActionParamDef[];
|
|
16
|
+
resolve?: (value: Record<string, any> | null) => void;
|
|
17
|
+
}
|
|
18
|
+
interface ActionParamDialogProps {
|
|
19
|
+
state: ParamDialogState;
|
|
20
|
+
onOpenChange: (open: boolean) => void;
|
|
21
|
+
}
|
|
22
|
+
export declare function ActionParamDialog({ state, onOpenChange }: ActionParamDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* ActionParamDialog — Collects user input for action parameters before execution.
|
|
4
|
+
*
|
|
5
|
+
* Dynamically renders form fields from ActionParamDef[] definitions:
|
|
6
|
+
* - type: 'select' → Shadcn Select component
|
|
7
|
+
* - type: 'text' → Shadcn Input component
|
|
8
|
+
* - type: 'textarea' → Shadcn Textarea component
|
|
9
|
+
* - other types → Shadcn Input with appropriate HTML type
|
|
10
|
+
*
|
|
11
|
+
* Returns collected param values or null on cancel.
|
|
12
|
+
*/
|
|
13
|
+
import { useState, useEffect } from 'react';
|
|
14
|
+
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Button, Input, Label, Textarea, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@object-ui/components';
|
|
15
|
+
export function ActionParamDialog({ state, onOpenChange }) {
|
|
16
|
+
const [values, setValues] = useState({});
|
|
17
|
+
const [errors, setErrors] = useState({});
|
|
18
|
+
// Reset values when params change
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (state.open) {
|
|
21
|
+
const defaults = {};
|
|
22
|
+
for (const param of state.params) {
|
|
23
|
+
if (param.defaultValue !== undefined) {
|
|
24
|
+
defaults[param.name] = param.defaultValue;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
setValues(defaults);
|
|
28
|
+
setErrors({});
|
|
29
|
+
}
|
|
30
|
+
}, [state.open, state.params]);
|
|
31
|
+
const isMissingValue = (value) => {
|
|
32
|
+
if (value === undefined || value === null)
|
|
33
|
+
return true;
|
|
34
|
+
if (typeof value === 'string')
|
|
35
|
+
return value.trim() === '';
|
|
36
|
+
if (Array.isArray(value))
|
|
37
|
+
return value.length === 0;
|
|
38
|
+
return false;
|
|
39
|
+
};
|
|
40
|
+
const handleSubmit = () => {
|
|
41
|
+
// Validate required fields
|
|
42
|
+
const newErrors = {};
|
|
43
|
+
for (const param of state.params) {
|
|
44
|
+
if (param.required && isMissingValue(values[param.name])) {
|
|
45
|
+
newErrors[param.name] = true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (Object.keys(newErrors).length > 0) {
|
|
49
|
+
setErrors(newErrors);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
state.resolve?.(values);
|
|
53
|
+
onOpenChange(false);
|
|
54
|
+
};
|
|
55
|
+
const handleCancel = () => {
|
|
56
|
+
state.resolve?.(null);
|
|
57
|
+
onOpenChange(false);
|
|
58
|
+
};
|
|
59
|
+
const updateValue = (name, value) => {
|
|
60
|
+
setValues(prev => ({ ...prev, [name]: value }));
|
|
61
|
+
setErrors(prev => ({ ...prev, [name]: false }));
|
|
62
|
+
};
|
|
63
|
+
return (_jsx(Dialog, { open: state.open, onOpenChange: (open) => {
|
|
64
|
+
if (!open)
|
|
65
|
+
handleCancel();
|
|
66
|
+
}, children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Action Parameters" }), _jsx(DialogDescription, { children: "Please provide the required information to continue." })] }), _jsx("div", { className: "grid gap-4 py-4", children: state.params.map((param) => (_jsxs("div", { className: "grid gap-2", children: [_jsxs(Label, { htmlFor: param.name, children: [param.label, param.required && _jsx("span", { className: "text-destructive ml-1", children: "*" })] }), param.type === 'select' && param.options ? (_jsxs(Select, { value: values[param.name] ?? '', onValueChange: (val) => updateValue(param.name, val), children: [_jsx(SelectTrigger, { id: param.name, className: errors[param.name] ? 'border-destructive' : '', children: _jsx(SelectValue, { placeholder: param.placeholder || `Select ${param.label}` }) }), _jsx(SelectContent, { children: param.options.map((opt) => (_jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value))) })] })) : param.type === 'textarea' ? (_jsx(Textarea, { id: param.name, value: values[param.name] ?? '', onChange: (e) => updateValue(param.name, e.target.value), placeholder: param.placeholder, className: errors[param.name] ? 'border-destructive' : '' })) : param.type === 'number' ? (_jsx(Input, { id: param.name, type: "number", value: values[param.name] ?? '', onChange: (e) => updateValue(param.name, e.target.value === '' ? undefined : e.target.valueAsNumber), placeholder: param.placeholder, className: errors[param.name] ? 'border-destructive' : '' })) : (_jsx(Input, { id: param.name, type: ['email', 'url', 'date', 'datetime-local', 'time', 'password'].includes(param.type) ? param.type : 'text', value: values[param.name] ?? '', onChange: (e) => updateValue(param.name, e.target.value), placeholder: param.placeholder, className: errors[param.name] ? 'border-destructive' : '' })), errors[param.name] && (_jsxs("p", { className: "text-xs text-destructive", children: [param.label, " is required"] })), param.helpText && (_jsx("p", { className: "text-xs text-muted-foreground", children: param.helpText }))] }, param.name))) }), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: handleCancel, children: "Cancel" }), _jsx(Button, { onClick: handleSubmit, children: "Confirm" })] })] }) }));
|
|
67
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard View Component
|
|
3
|
+
* Renders a dashboard based on the dashboardName parameter.
|
|
4
|
+
* Edit mode shows an inline config panel (DashboardConfigPanel / WidgetConfigPanel)
|
|
5
|
+
* on the right side, following the same pattern as ListView.
|
|
6
|
+
*/
|
|
7
|
+
export declare function DashboardView({ dataSource }: {
|
|
8
|
+
dataSource?: any;
|
|
9
|
+
}): import("react/jsx-runtime").JSX.Element;
|