@object-ui/app-shell 3.3.0 → 3.3.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.
Files changed (173) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +22 -2
  3. package/dist/chrome/CommandPalette.d.ts +16 -0
  4. package/dist/chrome/CommandPalette.js +88 -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 +217 -0
  101. package/dist/layout/AppSwitcher.d.ts +12 -0
  102. package/dist/layout/AppSwitcher.js +39 -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/ConsoleLayout.d.ts +21 -0
  108. package/dist/layout/ConsoleLayout.js +71 -0
  109. package/dist/layout/LocaleSwitcher.d.ts +7 -0
  110. package/dist/layout/LocaleSwitcher.js +27 -0
  111. package/dist/layout/ModeToggle.d.ts +1 -0
  112. package/dist/layout/ModeToggle.js +10 -0
  113. package/dist/layout/UnifiedSidebar.d.ts +22 -0
  114. package/dist/layout/UnifiedSidebar.js +210 -0
  115. package/dist/layout/index.d.ts +11 -0
  116. package/dist/layout/index.js +10 -0
  117. package/dist/providers/AdapterProvider.d.ts +21 -0
  118. package/dist/providers/AdapterProvider.js +52 -0
  119. package/dist/providers/ExpressionProvider.d.ts +50 -0
  120. package/dist/providers/ExpressionProvider.js +67 -0
  121. package/dist/providers/MetadataProvider.d.ts +18 -0
  122. package/dist/providers/MetadataProvider.js +324 -0
  123. package/dist/services/MetadataService.d.ts +119 -0
  124. package/dist/services/MetadataService.js +211 -0
  125. package/dist/skeletons/SkeletonDashboard.d.ts +13 -0
  126. package/dist/skeletons/SkeletonDashboard.js +12 -0
  127. package/dist/skeletons/SkeletonDetail.d.ts +15 -0
  128. package/dist/skeletons/SkeletonDetail.js +12 -0
  129. package/dist/skeletons/SkeletonGrid.d.ts +15 -0
  130. package/dist/skeletons/SkeletonGrid.js +12 -0
  131. package/dist/skeletons/index.d.ts +3 -0
  132. package/dist/skeletons/index.js +3 -0
  133. package/dist/utils/getIcon.d.ts +10 -0
  134. package/dist/utils/getIcon.js +24 -0
  135. package/dist/utils/index.d.ts +33 -0
  136. package/dist/utils/index.js +62 -0
  137. package/dist/utils/metadataConverters.d.ts +78 -0
  138. package/dist/utils/metadataConverters.js +74 -0
  139. package/dist/views/ActionConfirmDialog.d.ts +22 -0
  140. package/dist/views/ActionConfirmDialog.js +22 -0
  141. package/dist/views/ActionParamDialog.d.ts +23 -0
  142. package/dist/views/ActionParamDialog.js +67 -0
  143. package/dist/views/DashboardView.d.ts +9 -0
  144. package/dist/views/DashboardView.js +357 -0
  145. package/dist/views/DesignDrawer.d.ts +28 -0
  146. package/dist/views/DesignDrawer.js +51 -0
  147. package/dist/views/MetadataInspector.d.ts +40 -0
  148. package/dist/views/MetadataInspector.js +69 -0
  149. package/dist/views/ObjectView.d.ts +14 -0
  150. package/dist/views/ObjectView.js +770 -0
  151. package/dist/views/PageView.d.ts +6 -0
  152. package/dist/views/PageView.js +49 -0
  153. package/dist/views/RecordDetailView.d.ts +14 -0
  154. package/dist/views/RecordDetailView.js +508 -0
  155. package/dist/views/ReportView.d.ts +4 -0
  156. package/dist/views/ReportView.js +284 -0
  157. package/dist/views/SearchResultsPage.d.ts +9 -0
  158. package/dist/views/SearchResultsPage.js +107 -0
  159. package/dist/views/ViewConfigPanel.d.ts +56 -0
  160. package/dist/views/ViewConfigPanel.js +91 -0
  161. package/dist/views/index.d.ts +11 -0
  162. package/dist/views/index.js +11 -0
  163. package/package.json +59 -11
  164. package/.turbo/turbo-build.log +0 -4
  165. package/src/components/AppShell.tsx +0 -31
  166. package/src/components/DashboardRenderer.tsx +0 -36
  167. package/src/components/FormRenderer.tsx +0 -71
  168. package/src/components/ObjectRenderer.tsx +0 -122
  169. package/src/components/PageRenderer.tsx +0 -28
  170. package/src/index.ts +0 -20
  171. package/src/types.ts +0 -78
  172. package/tsconfig.json +0 -20
  173. package/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,210 @@
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 * as LucideIcons from 'lucide-react';
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
+ /**
89
+ * Resolve a Lucide icon component by name string.
90
+ * Safely handles both exact names and kebab-case → PascalCase conversion.
91
+ * The try/catch guards against strict module proxy environments (e.g. vitest mocks).
92
+ */
93
+ function getIcon(name) {
94
+ if (!name)
95
+ return LucideIcons.Database;
96
+ const lookup = (key) => {
97
+ try {
98
+ const icon = LucideIcons[key];
99
+ return typeof icon === 'function' ? icon : undefined;
100
+ }
101
+ catch {
102
+ return undefined;
103
+ }
104
+ };
105
+ // Try exact match first, then convert kebab-case / lowercase to PascalCase
106
+ const pascalName = name
107
+ .split(/[-_]/)
108
+ .map(part => part.charAt(0).toUpperCase() + part.slice(1))
109
+ .join('');
110
+ return lookup(name) ?? lookup(pascalName) ?? LucideIcons.Database;
111
+ }
112
+ export function UnifiedSidebar({ activeAppName }) {
113
+ const { isMobile } = useSidebar();
114
+ const location = useLocation();
115
+ const { t } = useObjectTranslation();
116
+ const { objectLabel: resolveNavObjectLabel } = useObjectLabel();
117
+ const { context, currentAppName } = useNavigationContext();
118
+ // Swipe-from-left-edge gesture to open sidebar on mobile
119
+ React.useEffect(() => {
120
+ const EDGE_THRESHOLD = 30;
121
+ const SWIPE_DISTANCE = 50;
122
+ let touchStartX = 0;
123
+ const handleTouchStart = (e) => {
124
+ touchStartX = e.touches[0].clientX;
125
+ };
126
+ const handleTouchEnd = (e) => {
127
+ const deltaX = e.changedTouches[0].clientX - touchStartX;
128
+ if (touchStartX < EDGE_THRESHOLD && deltaX > SWIPE_DISTANCE && isMobile) {
129
+ document.querySelector('[data-sidebar="trigger"]')?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
130
+ }
131
+ };
132
+ document.addEventListener('touchstart', handleTouchStart, { passive: true });
133
+ document.addEventListener('touchend', handleTouchEnd, { passive: true });
134
+ return () => {
135
+ document.removeEventListener('touchstart', handleTouchStart);
136
+ document.removeEventListener('touchend', handleTouchEnd);
137
+ };
138
+ }, [isMobile]);
139
+ const { recentItems } = useRecentItems();
140
+ const { favorites, removeFavorite } = useFavorites();
141
+ const { apps: metadataApps } = useMetadata();
142
+ const apps = metadataApps || [];
143
+ const activeApps = apps.filter((a) => a.active !== false);
144
+ const activeApp = activeApps.find((a) => a.name === (activeAppName || currentAppName)) || activeApps[0];
145
+ // Drag-reorder and pin persistence
146
+ const { applyOrder, handleReorder } = useNavOrder(activeApp?.name || 'home');
147
+ const { togglePin, applyPins } = useNavPins();
148
+ // Area management
149
+ const areas = activeApp?.areas || [];
150
+ const [activeAreaId, setActiveAreaId] = React.useState(() => areas.length > 0 ? areas[0].id : null);
151
+ React.useEffect(() => {
152
+ if (areas.length > 0) {
153
+ setActiveAreaId(prev => areas.some((a) => a.id === prev) ? prev : areas[0].id);
154
+ }
155
+ else {
156
+ setActiveAreaId(null);
157
+ }
158
+ }, [activeApp?.name, areas.length]);
159
+ // Resolve navigation items
160
+ const activeArea = areas.find((a) => a.id === activeAreaId);
161
+ const appNavigation = activeArea?.navigation || activeApp?.navigation || [];
162
+ // Home navigation items
163
+ const homeNavigation = React.useMemo(() => [
164
+ { id: 'home-dashboard', label: t('home.nav', { defaultValue: 'Home' }), type: 'url', url: '/home', icon: 'home' },
165
+ ], [t]);
166
+ // Determine which navigation to show based on context
167
+ const navigationItems = context === 'home' ? homeNavigation : appNavigation;
168
+ // Apply saved order and pin state
169
+ const processedNavigation = React.useMemo(() => {
170
+ const ordered = applyOrder(navigationItems);
171
+ return applyPins(ordered);
172
+ }, [navigationItems, applyOrder, applyPins]);
173
+ // Recent section collapsed by default
174
+ const [recentExpanded, setRecentExpanded] = React.useState(false);
175
+ // Visibility evaluation
176
+ const { evaluator } = useExpressionContext();
177
+ const evalVis = React.useCallback((expr) => evaluateVisibility(expr, evaluator), [evaluator]);
178
+ // Permission check
179
+ const { can } = usePermissions();
180
+ const checkPerm = React.useCallback((permissions) => permissions.every((perm) => {
181
+ const parts = perm.split(':');
182
+ const [object, action] = parts.length >= 2
183
+ ? [parts[0], parts[1]]
184
+ : [perm, 'read'];
185
+ return can(object, action);
186
+ }), [can]);
187
+ const basePath = context === 'app' && activeApp ? `/apps/${activeApp.name}` : '';
188
+ 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) => {
189
+ const AreaIcon = getIcon(area.icon);
190
+ const isActiveArea = area.id === activeAreaId;
191
+ 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));
192
+ }) }) })] })), _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) => {
193
+ const NavIcon = getIcon(item.icon);
194
+ const isActive = location.pathname === item.url;
195
+ 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));
196
+ }) }) }) }), 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) => {
197
+ const NavIcon = getIcon(item.icon);
198
+ let href = item.url || '#';
199
+ if (item.type === 'object') {
200
+ href = `${basePath}/${item.objectName}`;
201
+ if (item.viewName)
202
+ href += `/view/${item.viewName}`;
203
+ }
204
+ else if (item.type === 'dashboard')
205
+ href = item.dashboardName ? `${basePath}/dashboard/${item.dashboardName}` : '#';
206
+ else if (item.type === 'page')
207
+ href = item.pageName ? `${basePath}/page/${item.pageName}` : '#';
208
+ 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));
209
+ }) }))] }));
210
+ }
@@ -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
+ };