feat: upgrade design system to v0.1.19, use onNavigate/fillHeight, add SonarQube workflow
- Use Sidebar onNavigate callback instead of display:contents click interception - Use DataTable fillHeight prop instead of manual scroll wrapper divs - Fix DataTable scroll/pagination by adding overflow:hidden to content container - Fix left panel in split view to use flex column instead of overflow:auto - Make error tab stack trace scrollable for large traces - Add nightly SonarQube workflow with manual trigger support Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -447,6 +447,11 @@
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.errorStackWrap pre {
|
||||
max-height: 50vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.errorStackLabel {
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
|
||||
@@ -38,7 +38,9 @@ export function ErrorTab({ processor, executionDetail }: ErrorTabProps) {
|
||||
{errorStackTrace && (
|
||||
<>
|
||||
<div className={styles.errorStackLabel}>Stack Trace</div>
|
||||
<CodeBlock content={errorStackTrace} copyable />
|
||||
<div className={styles.errorStackWrap}>
|
||||
<CodeBlock content={errorStackTrace} copyable />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -222,36 +222,31 @@ function LayoutContent() {
|
||||
navigate(`${baseParts.join('/')}?text=${encodeURIComponent(query)}`);
|
||||
}, [navigate, scope.appId, scope.routeId]);
|
||||
|
||||
// Intercept Sidebar's internal <Link> navigation to re-route through current tab
|
||||
const handleSidebarClick = useCallback((e: React.MouseEvent) => {
|
||||
const anchor = (e.target as HTMLElement).closest('a[href]');
|
||||
if (!anchor) return;
|
||||
const href = anchor.getAttribute('href') || '';
|
||||
|
||||
// Intercept /apps/:appId and /apps/:appId/:routeId links
|
||||
const appMatch = href.match(/^\/apps\/([^/]+)(?:\/(.+))?$/);
|
||||
// Translate Sidebar's internal paths to our URL structure
|
||||
const handleSidebarNavigate = useCallback((path: string) => {
|
||||
// /apps/:appId and /apps/:appId/:routeId → current tab
|
||||
const appMatch = path.match(/^\/apps\/([^/]+)(?:\/(.+))?$/);
|
||||
if (appMatch) {
|
||||
e.preventDefault();
|
||||
const [, sAppId, sRouteId] = appMatch;
|
||||
navigate(sRouteId ? `/${scope.tab}/${sAppId}/${sRouteId}` : `/${scope.tab}/${sAppId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Intercept /agents/* links — redirect to runtime tab
|
||||
const agentMatch = href.match(/^\/agents\/([^/]+)(?:\/(.+))?$/);
|
||||
// /agents/:appId/:instanceId → runtime tab
|
||||
const agentMatch = path.match(/^\/agents\/([^/]+)(?:\/(.+))?$/);
|
||||
if (agentMatch) {
|
||||
e.preventDefault();
|
||||
const [, sAppId, sInstanceId] = agentMatch;
|
||||
navigate(sInstanceId ? `/runtime/${sAppId}/${sInstanceId}` : `/runtime/${sAppId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
navigate(path);
|
||||
}, [navigate, scope.tab]);
|
||||
|
||||
return (
|
||||
<AppShell
|
||||
sidebar={
|
||||
<div onClick={handleSidebarClick} style={{ display: 'contents' }}>
|
||||
<Sidebar apps={sidebarApps} />
|
||||
</div>
|
||||
<Sidebar apps={sidebarApps} onNavigate={handleSidebarNavigate} />
|
||||
}
|
||||
>
|
||||
<TopBar
|
||||
|
||||
@@ -5,25 +5,8 @@
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
background: var(--bg-body);
|
||||
}
|
||||
|
||||
/* Table section — stretches to fill and scrolls internally */
|
||||
|
||||
|
||||
.tableSection {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
background: var(--bg-surface);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tableScroll {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
background: var(--bg-body);
|
||||
}
|
||||
|
||||
.tableHeader {
|
||||
|
||||
@@ -236,60 +236,53 @@ export default function Dashboard({ onExchangeSelect }: DashboardProps = {}) {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Scrollable content */}
|
||||
<div className={styles.content}>
|
||||
{/* Exchanges table */}
|
||||
<div className={styles.tableSection}>
|
||||
<div className={styles.tableHeader}>
|
||||
<span className={styles.tableTitle}>
|
||||
{textFilter ? (
|
||||
<>
|
||||
<Search size={14} style={{ marginRight: 4, verticalAlign: -2 }} />
|
||||
Search: “{textFilter}”
|
||||
<button
|
||||
className={styles.clearSearch}
|
||||
onClick={() => setSearchParams({})}
|
||||
title="Clear search"
|
||||
>
|
||||
<X size={12} />
|
||||
</button>
|
||||
</>
|
||||
) : 'Recent Exchanges'}
|
||||
</span>
|
||||
<div className={styles.tableRight}>
|
||||
<span className={styles.tableMeta}>
|
||||
{rows.length.toLocaleString()} of {(searchResult?.total ?? 0).toLocaleString()} exchanges
|
||||
</span>
|
||||
{!textFilter && <Badge label="LIVE" color="success" />}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.tableScroll}>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={rows}
|
||||
onRowClick={handleRowClick}
|
||||
selectedId={selectedId}
|
||||
sortable
|
||||
flush
|
||||
onSortChange={handleSortChange}
|
||||
rowAccent={handleRowAccent}
|
||||
expandedContent={(row: Row) =>
|
||||
row.errorMessage ? (
|
||||
<div className={styles.inlineError}>
|
||||
<span className={styles.inlineErrorIcon}><AlertTriangle size={14} /></span>
|
||||
<div>
|
||||
<div className={styles.inlineErrorText}>{row.errorMessage}</div>
|
||||
<div className={styles.inlineErrorHint}>Click to view full stack trace</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.tableHeader}>
|
||||
<span className={styles.tableTitle}>
|
||||
{textFilter ? (
|
||||
<>
|
||||
<Search size={14} style={{ marginRight: 4, verticalAlign: -2 }} />
|
||||
Search: “{textFilter}”
|
||||
<button
|
||||
className={styles.clearSearch}
|
||||
onClick={() => setSearchParams({})}
|
||||
title="Clear search"
|
||||
>
|
||||
<X size={12} />
|
||||
</button>
|
||||
</>
|
||||
) : 'Recent Exchanges'}
|
||||
</span>
|
||||
<div className={styles.tableRight}>
|
||||
<span className={styles.tableMeta}>
|
||||
{rows.length.toLocaleString()} of {(searchResult?.total ?? 0).toLocaleString()} exchanges
|
||||
</span>
|
||||
{!textFilter && <Badge label="LIVE" color="success" />}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={rows}
|
||||
onRowClick={handleRowClick}
|
||||
selectedId={selectedId}
|
||||
sortable
|
||||
flush
|
||||
fillHeight
|
||||
onSortChange={handleSortChange}
|
||||
rowAccent={handleRowAccent}
|
||||
expandedContent={(row: Row) =>
|
||||
row.errorMessage ? (
|
||||
<div className={styles.inlineError}>
|
||||
<span className={styles.inlineErrorIcon}><AlertTriangle size={14} /></span>
|
||||
<div>
|
||||
<div className={styles.inlineErrorText}>{row.errorMessage}</div>
|
||||
<div className={styles.inlineErrorHint}>Click to view full stack trace</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
}
|
||||
|
||||
.leftPanel {
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user