@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.
Files changed (175) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/README.md +22 -2
  3. package/dist/chrome/CommandPalette.d.ts +16 -0
  4. package/dist/chrome/CommandPalette.js +74 -0
  5. package/dist/chrome/ConditionalAuthWrapper.d.ts +23 -0
  6. package/dist/chrome/ConditionalAuthWrapper.js +135 -0
  7. package/dist/chrome/ConsoleToaster.d.ts +11 -0
  8. package/dist/chrome/ConsoleToaster.js +28 -0
  9. package/dist/chrome/ErrorBoundary.d.ts +39 -0
  10. package/dist/chrome/ErrorBoundary.js +64 -0
  11. package/dist/chrome/KeyboardShortcutsDialog.d.ts +7 -0
  12. package/dist/chrome/KeyboardShortcutsDialog.js +63 -0
  13. package/dist/chrome/LoadingScreen.d.ts +12 -0
  14. package/dist/chrome/LoadingScreen.js +44 -0
  15. package/dist/chrome/OnboardingWalkthrough.d.ts +9 -0
  16. package/dist/chrome/OnboardingWalkthrough.js +11 -0
  17. package/dist/chrome/ThemeProvider.d.ts +13 -0
  18. package/dist/chrome/ThemeProvider.js +37 -0
  19. package/dist/chrome/index.d.ts +8 -0
  20. package/dist/chrome/index.js +8 -0
  21. package/dist/console/AppContent.d.ts +26 -0
  22. package/dist/console/AppContent.js +266 -0
  23. package/dist/console/ConsoleShell.d.ts +66 -0
  24. package/dist/console/ConsoleShell.js +97 -0
  25. package/dist/console/auth/AuthPageLayout.d.ts +9 -0
  26. package/dist/console/auth/AuthPageLayout.js +4 -0
  27. package/dist/console/auth/ForgotPasswordPage.d.ts +4 -0
  28. package/dist/console/auth/ForgotPasswordPage.js +23 -0
  29. package/dist/console/auth/LoginPage.d.ts +4 -0
  30. package/dist/console/auth/LoginPage.js +24 -0
  31. package/dist/console/auth/RegisterPage.d.ts +4 -0
  32. package/dist/console/auth/RegisterPage.js +29 -0
  33. package/dist/console/auth/index.d.ts +4 -0
  34. package/dist/console/auth/index.js +4 -0
  35. package/dist/console/home/AppCard.d.ts +14 -0
  36. package/dist/console/home/AppCard.js +36 -0
  37. package/dist/console/home/HomeLayout.d.ts +15 -0
  38. package/dist/console/home/HomeLayout.js +20 -0
  39. package/dist/console/home/HomePage.d.ts +18 -0
  40. package/dist/console/home/HomePage.js +54 -0
  41. package/dist/console/home/QuickActions.d.ts +9 -0
  42. package/dist/console/home/QuickActions.js +53 -0
  43. package/dist/console/home/RecentApps.d.ts +13 -0
  44. package/dist/console/home/RecentApps.js +32 -0
  45. package/dist/console/home/StarredApps.d.ts +13 -0
  46. package/dist/console/home/StarredApps.js +29 -0
  47. package/dist/console/home/index.d.ts +11 -0
  48. package/dist/console/home/index.js +11 -0
  49. package/dist/console/organizations/CreateWorkspaceDialog.d.ts +16 -0
  50. package/dist/console/organizations/CreateWorkspaceDialog.js +71 -0
  51. package/dist/console/organizations/OrganizationsLayout.d.ts +15 -0
  52. package/dist/console/organizations/OrganizationsLayout.js +20 -0
  53. package/dist/console/organizations/OrganizationsPage.d.ts +9 -0
  54. package/dist/console/organizations/OrganizationsPage.js +76 -0
  55. package/dist/console/organizations/index.d.ts +8 -0
  56. package/dist/console/organizations/index.js +8 -0
  57. package/dist/console/organizations/manage/AcceptInvitationPage.d.ts +7 -0
  58. package/dist/console/organizations/manage/AcceptInvitationPage.js +100 -0
  59. package/dist/console/organizations/manage/InvitationsPage.d.ts +6 -0
  60. package/dist/console/organizations/manage/InvitationsPage.js +107 -0
  61. package/dist/console/organizations/manage/InviteMemberDialog.d.ts +16 -0
  62. package/dist/console/organizations/manage/InviteMemberDialog.js +83 -0
  63. package/dist/console/organizations/manage/MembersPage.d.ts +6 -0
  64. package/dist/console/organizations/manage/MembersPage.js +89 -0
  65. package/dist/console/organizations/manage/OrganizationLayout.d.ts +8 -0
  66. package/dist/console/organizations/manage/OrganizationLayout.js +51 -0
  67. package/dist/console/organizations/manage/SettingsPage.d.ts +6 -0
  68. package/dist/console/organizations/manage/SettingsPage.js +141 -0
  69. package/dist/console/organizations/manage/orgContext.d.ts +5 -0
  70. package/dist/console/organizations/manage/orgContext.js +4 -0
  71. package/dist/context/FavoritesProvider.d.ts +50 -0
  72. package/dist/context/FavoritesProvider.js +129 -0
  73. package/dist/context/NavigationContext.d.ts +46 -0
  74. package/dist/context/NavigationContext.js +59 -0
  75. package/dist/context/index.d.ts +3 -0
  76. package/dist/context/index.js +2 -0
  77. package/dist/hooks/index.d.ts +7 -0
  78. package/dist/hooks/index.js +7 -0
  79. package/dist/hooks/useFavorites.d.ts +13 -0
  80. package/dist/hooks/useFavorites.js +13 -0
  81. package/dist/hooks/useMetadataService.d.ts +13 -0
  82. package/dist/hooks/useMetadataService.js +18 -0
  83. package/dist/hooks/useNavPins.d.ts +16 -0
  84. package/dist/hooks/useNavPins.js +72 -0
  85. package/dist/hooks/useNavigationSync.d.ts +82 -0
  86. package/dist/hooks/useNavigationSync.js +495 -0
  87. package/dist/hooks/useObjectActions.d.ts +38 -0
  88. package/dist/hooks/useObjectActions.js +92 -0
  89. package/dist/hooks/useRecentItems.d.ts +21 -0
  90. package/dist/hooks/useRecentItems.js +50 -0
  91. package/dist/hooks/useResponsiveSidebar.d.ts +8 -0
  92. package/dist/hooks/useResponsiveSidebar.js +28 -0
  93. package/dist/index.d.ts +29 -0
  94. package/dist/index.js +35 -0
  95. package/dist/layout/ActivityFeed.d.ts +22 -0
  96. package/dist/layout/ActivityFeed.js +57 -0
  97. package/dist/layout/AppHeader.d.ts +33 -0
  98. package/dist/layout/AppHeader.js +182 -0
  99. package/dist/layout/AppSidebar.d.ts +14 -0
  100. package/dist/layout/AppSidebar.js +202 -0
  101. package/dist/layout/AppSwitcher.d.ts +12 -0
  102. package/dist/layout/AppSwitcher.js +24 -0
  103. package/dist/layout/AuthPageLayout.d.ts +9 -0
  104. package/dist/layout/AuthPageLayout.js +4 -0
  105. package/dist/layout/ConnectionStatus.d.ts +14 -0
  106. package/dist/layout/ConnectionStatus.js +36 -0
  107. package/dist/layout/ConsoleFloatingChatbot.d.ts +10 -0
  108. package/dist/layout/ConsoleFloatingChatbot.js +27 -0
  109. package/dist/layout/ConsoleLayout.d.ts +22 -0
  110. package/dist/layout/ConsoleLayout.js +49 -0
  111. package/dist/layout/LocaleSwitcher.d.ts +7 -0
  112. package/dist/layout/LocaleSwitcher.js +27 -0
  113. package/dist/layout/ModeToggle.d.ts +1 -0
  114. package/dist/layout/ModeToggle.js +10 -0
  115. package/dist/layout/UnifiedSidebar.d.ts +27 -0
  116. package/dist/layout/UnifiedSidebar.js +186 -0
  117. package/dist/layout/index.d.ts +11 -0
  118. package/dist/layout/index.js +10 -0
  119. package/dist/providers/AdapterProvider.d.ts +21 -0
  120. package/dist/providers/AdapterProvider.js +52 -0
  121. package/dist/providers/ExpressionProvider.d.ts +50 -0
  122. package/dist/providers/ExpressionProvider.js +67 -0
  123. package/dist/providers/MetadataProvider.d.ts +18 -0
  124. package/dist/providers/MetadataProvider.js +324 -0
  125. package/dist/services/MetadataService.d.ts +119 -0
  126. package/dist/services/MetadataService.js +211 -0
  127. package/dist/skeletons/SkeletonDashboard.d.ts +13 -0
  128. package/dist/skeletons/SkeletonDashboard.js +12 -0
  129. package/dist/skeletons/SkeletonDetail.d.ts +15 -0
  130. package/dist/skeletons/SkeletonDetail.js +12 -0
  131. package/dist/skeletons/SkeletonGrid.d.ts +15 -0
  132. package/dist/skeletons/SkeletonGrid.js +12 -0
  133. package/dist/skeletons/index.d.ts +3 -0
  134. package/dist/skeletons/index.js +3 -0
  135. package/dist/utils/getIcon.d.ts +24 -0
  136. package/dist/utils/getIcon.js +87 -0
  137. package/dist/utils/index.d.ts +33 -0
  138. package/dist/utils/index.js +62 -0
  139. package/dist/utils/metadataConverters.d.ts +78 -0
  140. package/dist/utils/metadataConverters.js +74 -0
  141. package/dist/views/ActionConfirmDialog.d.ts +22 -0
  142. package/dist/views/ActionConfirmDialog.js +22 -0
  143. package/dist/views/ActionParamDialog.d.ts +23 -0
  144. package/dist/views/ActionParamDialog.js +67 -0
  145. package/dist/views/DashboardView.d.ts +9 -0
  146. package/dist/views/DashboardView.js +357 -0
  147. package/dist/views/DesignDrawer.d.ts +28 -0
  148. package/dist/views/DesignDrawer.js +51 -0
  149. package/dist/views/MetadataInspector.d.ts +40 -0
  150. package/dist/views/MetadataInspector.js +69 -0
  151. package/dist/views/ObjectView.d.ts +11 -0
  152. package/dist/views/ObjectView.js +769 -0
  153. package/dist/views/PageView.d.ts +6 -0
  154. package/dist/views/PageView.js +49 -0
  155. package/dist/views/RecordDetailView.d.ts +14 -0
  156. package/dist/views/RecordDetailView.js +508 -0
  157. package/dist/views/ReportView.d.ts +4 -0
  158. package/dist/views/ReportView.js +285 -0
  159. package/dist/views/SearchResultsPage.d.ts +9 -0
  160. package/dist/views/SearchResultsPage.js +107 -0
  161. package/dist/views/ViewConfigPanel.d.ts +56 -0
  162. package/dist/views/ViewConfigPanel.js +91 -0
  163. package/dist/views/index.d.ts +11 -0
  164. package/dist/views/index.js +11 -0
  165. package/package.json +59 -11
  166. package/.turbo/turbo-build.log +0 -4
  167. package/src/components/AppShell.tsx +0 -31
  168. package/src/components/DashboardRenderer.tsx +0 -36
  169. package/src/components/FormRenderer.tsx +0 -71
  170. package/src/components/ObjectRenderer.tsx +0 -122
  171. package/src/components/PageRenderer.tsx +0 -28
  172. package/src/index.ts +0 -20
  173. package/src/types.ts +0 -78
  174. package/tsconfig.json +0 -20
  175. package/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,186 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /**
3
+ * UnifiedSidebar
4
+ *
5
+ * Airtable-style contextual sidebar that dynamically switches between Home and App navigation.
6
+ * Features:
7
+ * - Persistent across all authenticated routes
8
+ * - Context-aware navigation (Home vs App)
9
+ * - Pinned bottom area (Settings, Help, User Profile)
10
+ * - Smooth transitions between contexts
11
+ * - Back to Home navigation from App context
12
+ * - App switcher dropdown
13
+ *
14
+ * @module
15
+ */
16
+ import * as React from 'react';
17
+ import { Link, useLocation } from 'react-router-dom';
18
+ import { getIcon } from '../utils/getIcon';
19
+ import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarGroupContent, SidebarMenu, SidebarMenuItem, SidebarMenuButton, SidebarMenuAction, SidebarTrigger, useSidebar, } from '@object-ui/components';
20
+ import { Clock, Star, StarOff, ChevronRight, Layers, } from 'lucide-react';
21
+ import { NavigationRenderer } from '@object-ui/layout';
22
+ import { useMetadata } from '../providers/MetadataProvider';
23
+ import { useExpressionContext, evaluateVisibility } from '../providers/ExpressionProvider';
24
+ import { usePermissions } from '@object-ui/permissions';
25
+ import { useRecentItems } from '../hooks/useRecentItems';
26
+ import { useFavorites } from '../hooks/useFavorites';
27
+ import { useNavPins } from '../hooks/useNavPins';
28
+ import { resolveI18nLabel } from '../utils';
29
+ import { useObjectTranslation, useObjectLabel } from '@object-ui/i18n';
30
+ // useObjectLabel provides appLabel/appDescription for convention-based
31
+ // i18n lookup — `{ns}.apps.{name}.label` resolves to the translated label
32
+ // loaded from /api/v1/i18n/translations/:locale.
33
+ import { useNavigationContext } from '../context/NavigationContext';
34
+ // ---------------------------------------------------------------------------
35
+ // useNavOrder – localStorage-persisted drag-and-drop reorder for nav items
36
+ // ---------------------------------------------------------------------------
37
+ function useNavOrder(appName) {
38
+ const storageKey = `objectui-nav-order-${appName}`;
39
+ const [orderMap, setOrderMap] = React.useState(() => {
40
+ try {
41
+ const raw = localStorage.getItem(storageKey);
42
+ if (!raw)
43
+ return {};
44
+ const parsed = JSON.parse(raw);
45
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
46
+ return {};
47
+ const result = {};
48
+ for (const [k, v] of Object.entries(parsed)) {
49
+ if (Array.isArray(v) && v.every((i) => typeof i === 'string')) {
50
+ result[k] = v;
51
+ }
52
+ }
53
+ return result;
54
+ }
55
+ catch {
56
+ return {};
57
+ }
58
+ });
59
+ const persist = React.useCallback((next) => {
60
+ setOrderMap(next);
61
+ try {
62
+ localStorage.setItem(storageKey, JSON.stringify(next));
63
+ }
64
+ catch { /* full */ }
65
+ }, [storageKey]);
66
+ const applyOrder = React.useCallback((items) => {
67
+ const saved = orderMap['__root__'];
68
+ if (!saved)
69
+ return items;
70
+ const byId = new Map(items.map(i => [i.id, i]));
71
+ const ordered = [];
72
+ for (const id of saved) {
73
+ const item = byId.get(id);
74
+ if (item) {
75
+ ordered.push(item);
76
+ byId.delete(id);
77
+ }
78
+ }
79
+ byId.forEach(item => ordered.push(item));
80
+ return ordered;
81
+ }, [orderMap]);
82
+ const handleReorder = React.useCallback((reorderedItems) => {
83
+ const ids = reorderedItems.map(i => i.id);
84
+ persist({ ...orderMap, __root__: ids });
85
+ }, [orderMap, persist]);
86
+ return { applyOrder, handleReorder };
87
+ }
88
+ export function UnifiedSidebar({ activeAppName }) {
89
+ const { isMobile } = useSidebar();
90
+ const location = useLocation();
91
+ const { t } = useObjectTranslation();
92
+ const { objectLabel: resolveNavObjectLabel } = useObjectLabel();
93
+ const { context, currentAppName } = useNavigationContext();
94
+ // Swipe-from-left-edge gesture to open sidebar on mobile
95
+ React.useEffect(() => {
96
+ const EDGE_THRESHOLD = 30;
97
+ const SWIPE_DISTANCE = 50;
98
+ let touchStartX = 0;
99
+ const handleTouchStart = (e) => {
100
+ touchStartX = e.touches[0].clientX;
101
+ };
102
+ const handleTouchEnd = (e) => {
103
+ const deltaX = e.changedTouches[0].clientX - touchStartX;
104
+ if (touchStartX < EDGE_THRESHOLD && deltaX > SWIPE_DISTANCE && isMobile) {
105
+ document.querySelector('[data-sidebar="trigger"]')?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
106
+ }
107
+ };
108
+ document.addEventListener('touchstart', handleTouchStart, { passive: true });
109
+ document.addEventListener('touchend', handleTouchEnd, { passive: true });
110
+ return () => {
111
+ document.removeEventListener('touchstart', handleTouchStart);
112
+ document.removeEventListener('touchend', handleTouchEnd);
113
+ };
114
+ }, [isMobile]);
115
+ const { recentItems } = useRecentItems();
116
+ const { favorites, removeFavorite } = useFavorites();
117
+ const { apps: metadataApps } = useMetadata();
118
+ const apps = metadataApps || [];
119
+ const activeApps = apps.filter((a) => a.active !== false);
120
+ const activeApp = activeApps.find((a) => a.name === (activeAppName || currentAppName)) || activeApps[0];
121
+ // Drag-reorder and pin persistence
122
+ const { applyOrder, handleReorder } = useNavOrder(activeApp?.name || 'home');
123
+ const { togglePin, applyPins } = useNavPins();
124
+ // Area management
125
+ const areas = activeApp?.areas || [];
126
+ const [activeAreaId, setActiveAreaId] = React.useState(() => areas.length > 0 ? areas[0].id : null);
127
+ React.useEffect(() => {
128
+ if (areas.length > 0) {
129
+ setActiveAreaId(prev => areas.some((a) => a.id === prev) ? prev : areas[0].id);
130
+ }
131
+ else {
132
+ setActiveAreaId(null);
133
+ }
134
+ }, [activeApp?.name, areas.length]);
135
+ // Resolve navigation items
136
+ const activeArea = areas.find((a) => a.id === activeAreaId);
137
+ const appNavigation = activeArea?.navigation || activeApp?.navigation || [];
138
+ // Home navigation items
139
+ const homeNavigation = React.useMemo(() => [
140
+ { id: 'home-dashboard', label: t('home.nav', { defaultValue: 'Home' }), type: 'url', url: '/home', icon: 'home' },
141
+ ], [t]);
142
+ // Determine which navigation to show based on context
143
+ const navigationItems = context === 'home' ? homeNavigation : appNavigation;
144
+ // Apply saved order and pin state
145
+ const processedNavigation = React.useMemo(() => {
146
+ const ordered = applyOrder(navigationItems);
147
+ return applyPins(ordered);
148
+ }, [navigationItems, applyOrder, applyPins]);
149
+ // Recent section collapsed by default
150
+ const [recentExpanded, setRecentExpanded] = React.useState(false);
151
+ // Visibility evaluation
152
+ const { evaluator } = useExpressionContext();
153
+ const evalVis = React.useCallback((expr) => evaluateVisibility(expr, evaluator), [evaluator]);
154
+ // Permission check
155
+ const { can } = usePermissions();
156
+ const checkPerm = React.useCallback((permissions) => permissions.every((perm) => {
157
+ const parts = perm.split(':');
158
+ const [object, action] = parts.length >= 2
159
+ ? [parts[0], parts[1]]
160
+ : [perm, 'read'];
161
+ return can(object, action);
162
+ }), [can]);
163
+ const basePath = context === 'app' && activeApp ? `/apps/${activeApp.name}` : '';
164
+ return (_jsxs(_Fragment, { children: [_jsxs(Sidebar, { collapsible: "icon", className: "!top-14 !h-[calc(100svh-3.5rem)]", children: [_jsx(SidebarContent, { className: "pt-2", children: _jsx("div", { className: "transition-opacity duration-200 ease-in-out", children: context === 'app' && activeApp ? (_jsxs(_Fragment, { children: [areas.length > 1 && (_jsxs(SidebarGroup, { children: [_jsxs(SidebarGroupLabel, { className: "flex items-center gap-1.5", children: [_jsx(Layers, { className: "h-3.5 w-3.5" }), "Area"] }), _jsx(SidebarGroupContent, { children: _jsx(SidebarMenu, { children: areas.map((area) => {
165
+ const AreaIcon = getIcon(area.icon);
166
+ const isActiveArea = area.id === activeAreaId;
167
+ return (_jsx(SidebarMenuItem, { children: _jsxs(SidebarMenuButton, { isActive: isActiveArea, tooltip: area.label, onClick: () => setActiveAreaId(area.id), children: [_jsx(AreaIcon, { className: "h-4 w-4" }), _jsx("span", { children: area.label })] }) }, area.id));
168
+ }) }) })] })), _jsx(NavigationRenderer, { items: processedNavigation, basePath: basePath, evaluateVisibility: evalVis, checkPermission: checkPerm, enablePinning: true, onPinToggle: togglePin, enableReorder: true, onReorder: handleReorder, resolveObjectLabel: (objectName, fallback) => resolveNavObjectLabel({ name: objectName, label: fallback }), t: t }), recentItems.length > 0 && (_jsxs(SidebarGroup, { children: [_jsxs(SidebarGroupLabel, { className: "flex items-center gap-1.5 cursor-pointer select-none", onClick: () => setRecentExpanded(prev => !prev), children: [_jsx(ChevronRight, { className: `h-3 w-3 transition-transform duration-150 ${recentExpanded ? 'rotate-90' : ''}` }), _jsx(Clock, { className: "h-3.5 w-3.5" }), "Recent"] }), recentExpanded && (_jsx(SidebarGroupContent, { children: _jsx(SidebarMenu, { children: recentItems.slice(0, 5).map(item => (_jsx(SidebarMenuItem, { children: _jsx(SidebarMenuButton, { asChild: true, tooltip: item.label, children: _jsxs(Link, { to: item.href, children: [_jsx("span", { className: "text-muted-foreground", children: item.type === 'dashboard' ? '📊' : item.type === 'report' ? '📈' : '📄' }), _jsx("span", { className: "truncate", children: item.label })] }) }) }, item.id))) }) }))] })), favorites.length > 0 && (_jsxs(SidebarGroup, { children: [_jsxs(SidebarGroupLabel, { className: "flex items-center gap-1.5", children: [_jsx(Star, { className: "h-3.5 w-3.5" }), "Favorites"] }), _jsx(SidebarGroupContent, { children: _jsx(SidebarMenu, { children: favorites.slice(0, 8).map(item => (_jsxs(SidebarMenuItem, { children: [_jsx(SidebarMenuButton, { asChild: true, tooltip: item.label, children: _jsxs(Link, { to: item.href, children: [_jsx("span", { className: "text-muted-foreground", children: item.type === 'dashboard' ? '📊' : item.type === 'report' ? '📈' : item.type === 'page' ? '📄' : '📋' }), _jsx("span", { className: "truncate", children: item.label })] }) }), _jsx(SidebarMenuAction, { showOnHover: true, onClick: (e) => { e.stopPropagation(); removeFavorite(item.id); }, "aria-label": `Remove ${item.label} from favorites`, children: _jsx(StarOff, { className: "h-3 w-3" }) })] }, item.id))) }) })] }))] })) : (_jsxs(_Fragment, { children: [_jsx(SidebarGroup, { children: _jsx(SidebarGroupContent, { children: _jsx(SidebarMenu, { children: homeNavigation.map((item) => {
169
+ const NavIcon = getIcon(item.icon);
170
+ const isActive = location.pathname === item.url;
171
+ return (_jsx(SidebarMenuItem, { children: _jsx(SidebarMenuButton, { asChild: true, tooltip: item.label, isActive: isActive, children: _jsxs(Link, { to: item.url || '/home', children: [_jsx(NavIcon, { className: "h-4 w-4" }), _jsx("span", { children: item.label })] }) }) }, item.id));
172
+ }) }) }) }), favorites.filter(f => f.type === 'object' || f.type === 'dashboard' || f.type === 'page').length > 0 && (_jsxs(SidebarGroup, { children: [_jsxs(SidebarGroupLabel, { className: "flex items-center gap-1.5", children: [_jsx(Star, { className: "h-3.5 w-3.5" }), "Starred"] }), _jsx(SidebarGroupContent, { children: _jsx(SidebarMenu, { children: favorites.filter(f => f.type === 'object' || f.type === 'dashboard' || f.type === 'page').slice(0, 8).map(item => (_jsxs(SidebarMenuItem, { children: [_jsx(SidebarMenuButton, { asChild: true, tooltip: item.label, children: _jsxs(Link, { to: item.href, children: [_jsx("span", { className: "text-muted-foreground", children: item.type === 'dashboard' ? '📊' : item.type === 'page' ? '📄' : '📋' }), _jsx("span", { className: "truncate", children: item.label })] }) }), _jsx(SidebarMenuAction, { showOnHover: true, onClick: (e) => { e.stopPropagation(); removeFavorite(item.id); }, "aria-label": `Remove ${item.label} from favorites`, children: _jsx(StarOff, { className: "h-3 w-3" }) })] }, item.id))) }) })] }))] })) }) }), _jsx(SidebarFooter, { className: "border-t p-1", children: _jsx(SidebarTrigger, { className: "w-full justify-start pl-2 group-data-[state=collapsed]:justify-center group-data-[state=collapsed]:pl-0" }) })] }), isMobile && context === 'app' && (_jsx("div", { className: "fixed bottom-0 left-0 right-0 z-50 flex items-center justify-around border-t bg-background/95 backdrop-blur-sm px-2 py-1 sm:hidden safe-area-bottom", children: processedNavigation.filter((n) => n.type !== 'group').slice(0, 5).map((item) => {
173
+ const NavIcon = getIcon(item.icon);
174
+ let href = item.url || '#';
175
+ if (item.type === 'object') {
176
+ href = `${basePath}/${item.objectName}`;
177
+ if (item.viewName)
178
+ href += `/view/${item.viewName}`;
179
+ }
180
+ else if (item.type === 'dashboard')
181
+ href = item.dashboardName ? `${basePath}/dashboard/${item.dashboardName}` : '#';
182
+ else if (item.type === 'page')
183
+ href = item.pageName ? `${basePath}/page/${item.pageName}` : '#';
184
+ return (_jsxs(Link, { to: href, className: "flex flex-col items-center gap-0.5 px-2 py-1.5 text-muted-foreground hover:text-foreground transition-colors min-w-[44px] min-h-[44px] justify-center", children: [_jsx(NavIcon, { className: "h-5 w-5" }), _jsx("span", { className: "text-[10px] truncate max-w-[60px]", children: resolveI18nLabel(item.label, t) })] }, item.id));
185
+ }) }))] }));
186
+ }
@@ -0,0 +1,11 @@
1
+ export { ConsoleLayout } from './ConsoleLayout';
2
+ export { AppHeader } from './AppHeader';
3
+ export { AppSidebar } from './AppSidebar';
4
+ export { UnifiedSidebar } from './UnifiedSidebar';
5
+ export { AppSwitcher } from './AppSwitcher';
6
+ export { ConnectionStatus } from './ConnectionStatus';
7
+ export { ActivityFeed } from './ActivityFeed';
8
+ export type { ActivityItem } from './ActivityFeed';
9
+ export { LocaleSwitcher } from './LocaleSwitcher';
10
+ export { ModeToggle } from './ModeToggle';
11
+ export { AuthPageLayout } from './AuthPageLayout';
@@ -0,0 +1,10 @@
1
+ export { ConsoleLayout } from './ConsoleLayout';
2
+ export { AppHeader } from './AppHeader';
3
+ export { AppSidebar } from './AppSidebar';
4
+ export { UnifiedSidebar } from './UnifiedSidebar';
5
+ export { AppSwitcher } from './AppSwitcher';
6
+ export { ConnectionStatus } from './ConnectionStatus';
7
+ export { ActivityFeed } from './ActivityFeed';
8
+ export { LocaleSwitcher } from './LocaleSwitcher';
9
+ export { ModeToggle } from './ModeToggle';
10
+ export { AuthPageLayout } from './AuthPageLayout';
@@ -0,0 +1,21 @@
1
+ /**
2
+ * AdapterProvider
3
+ *
4
+ * Creates and provides an ObjectStackAdapter instance to the component tree.
5
+ * Also exposes a `useAdapter` hook for consuming the adapter in child components.
6
+ *
7
+ * @module
8
+ */
9
+ import { type ReactNode } from 'react';
10
+ import { ObjectStackAdapter } from '@object-ui/data-objectstack';
11
+ export { useAdapter } from '@object-ui/react';
12
+ interface AdapterProviderProps {
13
+ children: ReactNode;
14
+ /** Optional pre-created adapter (useful for testing). */
15
+ adapter?: ObjectStackAdapter | null;
16
+ }
17
+ /**
18
+ * Creates an ObjectStackAdapter, connects to the API, then provides it to children.
19
+ * Shows nothing (returns null) until the adapter is ready.
20
+ */
21
+ export declare function AdapterProvider({ children, adapter: externalAdapter }: AdapterProviderProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,52 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * AdapterProvider
4
+ *
5
+ * Creates and provides an ObjectStackAdapter instance to the component tree.
6
+ * Also exposes a `useAdapter` hook for consuming the adapter in child components.
7
+ *
8
+ * @module
9
+ */
10
+ import { useState, useEffect } from 'react';
11
+ import { ObjectStackAdapter } from '@object-ui/data-objectstack';
12
+ import { createAuthenticatedFetch } from '@object-ui/auth';
13
+ import { AdapterCtx } from '@object-ui/react';
14
+ export { useAdapter } from '@object-ui/react';
15
+ /**
16
+ * Creates an ObjectStackAdapter, connects to the API, then provides it to children.
17
+ * Shows nothing (returns null) until the adapter is ready.
18
+ */
19
+ export function AdapterProvider({ children, adapter: externalAdapter }) {
20
+ const [adapter, setAdapter] = useState(externalAdapter ?? null);
21
+ useEffect(() => {
22
+ if (externalAdapter) {
23
+ setAdapter(externalAdapter);
24
+ return;
25
+ }
26
+ let cancelled = false;
27
+ async function init() {
28
+ try {
29
+ const a = new ObjectStackAdapter({
30
+ baseUrl: import.meta.env.VITE_SERVER_URL || '',
31
+ fetch: createAuthenticatedFetch(),
32
+ autoReconnect: true,
33
+ maxReconnectAttempts: 5,
34
+ reconnectDelay: 1000,
35
+ cache: { maxSize: 50, ttl: 300000 },
36
+ });
37
+ await a.connect();
38
+ if (!cancelled) {
39
+ setAdapter(a);
40
+ }
41
+ }
42
+ catch (err) {
43
+ if (!cancelled) {
44
+ console.error('[Console] Failed to initialize:', err);
45
+ }
46
+ }
47
+ }
48
+ init();
49
+ return () => { cancelled = true; };
50
+ }, [externalAdapter]);
51
+ return (_jsx(AdapterCtx.Provider, { value: adapter, children: children }));
52
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * ExpressionContext Provider
3
+ *
4
+ * Provides expression evaluation context (user, app, data) to all child components.
5
+ * Used by useCondition/useExpression hooks from @object-ui/react to evaluate
6
+ * dynamic visibility, disabled, and hidden expressions in navigation items,
7
+ * fields, and components.
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * <ExpressionProvider user={currentUser} app={activeApp}>
12
+ * <AppSidebar />
13
+ * </ExpressionProvider>
14
+ * ```
15
+ */
16
+ import React from 'react';
17
+ import { ExpressionEvaluator } from '@object-ui/core';
18
+ export interface ExpressionContextValue {
19
+ /** Current authenticated user */
20
+ user: Record<string, any>;
21
+ /** Active application config */
22
+ app: Record<string, any>;
23
+ /** Additional data scope */
24
+ data: Record<string, any>;
25
+ /** The evaluator instance (for imperative use) */
26
+ evaluator: ExpressionEvaluator;
27
+ }
28
+ interface ExpressionProviderProps {
29
+ children: React.ReactNode;
30
+ user?: Record<string, any>;
31
+ app?: Record<string, any>;
32
+ data?: Record<string, any>;
33
+ }
34
+ export declare function ExpressionProvider({ children, user, app, data }: ExpressionProviderProps): import("react/jsx-runtime").JSX.Element;
35
+ /**
36
+ * Hook to access the expression context.
37
+ * Returns the full context value or a default empty context.
38
+ */
39
+ export declare function useExpressionContext(): ExpressionContextValue;
40
+ /**
41
+ * Evaluate a visibility expression.
42
+ * Supports:
43
+ * - boolean: true/false
44
+ * - string "true"/"false"
45
+ * - template expression: "${user.role === 'admin'}"
46
+ *
47
+ * Returns true if the item should be visible.
48
+ */
49
+ export declare function evaluateVisibility(expression: string | boolean | undefined, evaluator: ExpressionEvaluator): boolean;
50
+ export {};
@@ -0,0 +1,67 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * ExpressionContext Provider
4
+ *
5
+ * Provides expression evaluation context (user, app, data) to all child components.
6
+ * Used by useCondition/useExpression hooks from @object-ui/react to evaluate
7
+ * dynamic visibility, disabled, and hidden expressions in navigation items,
8
+ * fields, and components.
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * <ExpressionProvider user={currentUser} app={activeApp}>
13
+ * <AppSidebar />
14
+ * </ExpressionProvider>
15
+ * ```
16
+ */
17
+ import { createContext, useContext, useMemo } from 'react';
18
+ import { ExpressionEvaluator } from '@object-ui/core';
19
+ const ExprCtx = createContext(null);
20
+ export function ExpressionProvider({ children, user = {}, app = {}, data = {} }) {
21
+ const value = useMemo(() => {
22
+ const context = { user, app, data };
23
+ const evaluator = new ExpressionEvaluator(context);
24
+ return { user, app, data, evaluator };
25
+ }, [user, app, data]);
26
+ return _jsx(ExprCtx.Provider, { value: value, children: children });
27
+ }
28
+ /**
29
+ * Hook to access the expression context.
30
+ * Returns the full context value or a default empty context.
31
+ */
32
+ export function useExpressionContext() {
33
+ const ctx = useContext(ExprCtx);
34
+ if (!ctx) {
35
+ // Return a safe default so components can be used outside the provider
36
+ const fallback = { user: {}, app: {}, data: {} };
37
+ return { ...fallback, evaluator: new ExpressionEvaluator(fallback) };
38
+ }
39
+ return ctx;
40
+ }
41
+ /**
42
+ * Evaluate a visibility expression.
43
+ * Supports:
44
+ * - boolean: true/false
45
+ * - string "true"/"false"
46
+ * - template expression: "${user.role === 'admin'}"
47
+ *
48
+ * Returns true if the item should be visible.
49
+ */
50
+ export function evaluateVisibility(expression, evaluator) {
51
+ if (expression === undefined || expression === null)
52
+ return true;
53
+ if (expression === true || expression === 'true')
54
+ return true;
55
+ if (expression === false || expression === 'false')
56
+ return false;
57
+ if (typeof expression === 'string' && expression.includes('${')) {
58
+ try {
59
+ const result = evaluator.evaluateCondition(expression);
60
+ return result;
61
+ }
62
+ catch {
63
+ return true; // Default to visible on error
64
+ }
65
+ }
66
+ return true;
67
+ }
@@ -0,0 +1,18 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { ObjectStackAdapter } from '@object-ui/data-objectstack';
3
+ import { useMetadata, type MetadataContextValue, type MetadataState } from '@object-ui/react';
4
+ export type { MetadataState, MetadataContextValue };
5
+ export { useMetadataItem } from '@object-ui/react';
6
+ export { useMetadata };
7
+ export type MetadataTypeStatus = 'idle' | 'loading' | 'ready' | 'error';
8
+ interface MetadataProviderProps {
9
+ children: ReactNode;
10
+ adapter: ObjectStackAdapter;
11
+ ttlMs?: number;
12
+ }
13
+ export declare function MetadataProvider({ children, adapter, ttlMs }: MetadataProviderProps): import("react/jsx-runtime").JSX.Element;
14
+ export declare function useMetadataType(type: string): {
15
+ items: any[];
16
+ loading: boolean;
17
+ error: Error | null;
18
+ };