, select: (module: M) => ComponentType) {
+ return lazy(() => load().then((module) => ({ default: select(module) })));
+}
+
+const DashboardView = lazyFeature(
+ () => import('@/features/dashboard/components/DashboardView'),
+ (m) => m.DashboardView,
+);
+const DeliveryTracePage = lazyFeature(
+ () => import('@/features/troubleshoot/DeliveryTracePage'),
+ (m) => m.DeliveryTracePage,
+);
+const LiveTracingPage = lazyFeature(
+ () => import('@/features/tracing/components/LiveTracingPage'),
+ (m) => m.LiveTracingPage,
+);
+const TraceDetailView = lazyFeature(
+ () => import('@/features/tracing/components/TraceDetailView'),
+ (m) => m.TraceDetailView,
+);
+const ActionPage = lazyFeature(
+ () => import('@/features/actions/ActionPage'),
+ (m) => m.ActionPage,
+);
interface MainContentProps {
viewName?: string;
@@ -32,6 +54,10 @@ export function MainContent({ viewName, id, section }: MainContentProps) {
invalidateAllObjectLists();
}, [viewName, invalidateAllObjectLists]);
+ return }>{renderView(schema, viewName, id, section)};
+}
+
+function renderView(schema: Schema | null, viewName?: string, id?: string, section?: string): ReactNode {
if (!viewName) {
return (
Select a view from the sidebar.
@@ -84,7 +110,7 @@ export function MainContent({ viewName, id, section }: MainContentProps) {
}
if (id) {
- if (resolved.objectName === 'x:Trace' && id !== 'new') {
+ if (resolved.objectName === 'x:Trace') {
return ;
}
const canUpdate = useAccountStore.getState().hasObjectPermission(resolved.permissionPrefix, 'Update');
diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx
index d69be26..95cdf14 100644
--- a/src/components/layout/Sidebar.tsx
+++ b/src/components/layout/Sidebar.tsx
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
-import { useEffect, useMemo, useState } from 'react';
+import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import * as LucideIcons from 'lucide-react';
const { ChevronDown, Lock } = LucideIcons;
@@ -74,6 +74,25 @@ function subtreeHasVisibleLink(items: LayoutSubItem[], edition: string): boolean
return false;
}
+interface AutoOpenCollapsibleProps {
+ containsActive: boolean;
+ children: React.ReactNode;
+}
+
+function AutoOpenCollapsible({ containsActive, children }: AutoOpenCollapsibleProps) {
+ const [open, setOpen] = useState(containsActive);
+ const [prevContainsActive, setPrevContainsActive] = useState(containsActive);
+ if (containsActive !== prevContainsActive) {
+ setPrevContainsActive(containsActive);
+ if (containsActive) setOpen(true);
+ }
+ return (
+
+ {children}
+
+ );
+}
+
function checkLinkVisible(viewName: string): boolean {
const schema = useSchemaStore.getState().schema;
if (!schema) return true;
@@ -95,6 +114,8 @@ function checkIsEnterprise(viewName: string): boolean {
return isLinkEnterprise(schema, viewName, edition);
}
+type ActiveItemRef = (el: HTMLButtonElement | null) => void;
+
interface SidebarSubItemProps {
item: LayoutSubItem;
depth: number;
@@ -103,9 +124,19 @@ interface SidebarSubItemProps {
navigate: ReturnType;
edition: string;
onUpsell: () => void;
+ activeItemRef: ActiveItemRef;
}
-function SidebarSubItem({ item, depth, sectionName, currentPath, navigate, edition, onUpsell }: SidebarSubItemProps) {
+function SidebarSubItem({
+ item,
+ depth,
+ sectionName,
+ currentPath,
+ navigate,
+ edition,
+ onUpsell,
+ activeItemRef,
+}: SidebarSubItemProps) {
if (item.type === 'link') {
if (!checkLinkVisible(item.viewName)) return null;
@@ -120,6 +151,7 @@ function SidebarSubItem({ item, depth, sectionName, currentPath, navigate, editi
return (