Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/dark-webs-shave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"dashboard": minor
---

Add a beta Risk Events log under Logs for reviewing and managing policy-flagged or blocked findings.
81 changes: 81 additions & 0 deletions client/dashboard/src/components/log-workbench.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { cn } from "@/lib/utils";
import type React from "react";

export interface LogWorkbenchProps {
title: React.ReactNode;
description?: React.ReactNode;
actions?: React.ReactNode;
filters?: React.ReactNode;
status?: React.ReactNode;
header?: React.ReactNode;
children: React.ReactNode;
footer?: React.ReactNode;
detail?: React.ReactNode;
onScroll?: React.UIEventHandler<HTMLDivElement>;
scrollRef?: React.Ref<HTMLDivElement>;
surfaceClassName?: string;
contentClassName?: string;
className?: string;
}

export function LogWorkbench({
title,
description,
actions,
filters,
status,
header,
children,
footer,
detail,
onScroll,
scrollRef,
surfaceClassName,
contentClassName,
className,
}: LogWorkbenchProps) {
return (
<>
<div className={cn("flex min-h-0 w-full flex-1 flex-col", className)}>
<div className="shrink-0 px-8 py-4">
<div className="mb-4 flex items-start justify-between gap-4">
<div className="flex min-w-0 flex-col gap-1">
<h1 className="text-xl font-semibold">{title}</h1>
{description ? (
<p className="text-muted-foreground text-sm">{description}</p>
) : null}
</div>
{actions ? (
<div className="flex shrink-0 items-center gap-2">{actions}</div>
) : null}
</div>
{filters ? (
<div className="flex flex-wrap items-center gap-4">{filters}</div>
) : null}
</div>

<div className="min-h-0 flex-1 overflow-hidden border-t">
<div
className={cn(
"bg-background flex h-full flex-col",
surfaceClassName,
)}
>
{status}
{header}
<div
ref={scrollRef}
className={cn("flex-1 overflow-y-auto", contentClassName)}
onScroll={onScroll}
>
{children}
</div>
{footer}
</div>
</div>
</div>

{detail}
</>
);
}
31 changes: 28 additions & 3 deletions client/dashboard/src/components/observe/ObserveTabNav.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import { cn } from "@/lib/utils";
import { Link, useLocation } from "react-router";
import { useSlugs } from "@/contexts/Sdk";
import { RequireScope } from "@/components/require-scope";
import type { Scope } from "@/hooks/useRBAC";
import {
ReleaseStage,
ReleaseStageBadge,
} from "@/components/release-stage-badge";

type Tab = { label: string; href: string; stage?: ReleaseStage };
type Tab = {
label: string;
href: string;
stage?: ReleaseStage;
scope?: Scope | Scope[];
};

export function ObserveTabNav({ base }: { base: "insights" | "logs" }) {
const { orgSlug, projectSlug } = useSlugs();
Expand All @@ -17,7 +24,15 @@ export function ObserveTabNav({ base }: { base: "insights" | "logs" }) {
{ label: "Tools", href: `${baseSlug}/tools` },
{ label: "MCP Servers", href: `${baseSlug}/mcp` },
...(base === "logs"
? [{ label: "Agents", href: `${baseSlug}/agents` }]
? ([
{
label: "Risk Events",
href: `${baseSlug}/risk-events`,
stage: "beta",
scope: "org:admin",
},
{ label: "Agents", href: `${baseSlug}/agents` },
] satisfies Tab[])
: []),
...(base === "insights"
? ([
Expand All @@ -37,7 +52,7 @@ export function ObserveTabNav({ base }: { base: "insights" | "logs" }) {
const isActive =
location.pathname === tab.href ||
location.pathname.startsWith(tab.href + "/");
return (
const link = (
<Link
key={tab.href}
to={tab.href}
Expand All @@ -56,6 +71,16 @@ export function ObserveTabNav({ base }: { base: "insights" | "logs" }) {
)}
</Link>
);

if (tab.scope) {
return (
<RequireScope key={tab.href} scope={tab.scope} level="section">
{link}
</RequireScope>
);
}

return link;
})}
</div>
);
Expand Down
1 change: 1 addition & 0 deletions client/dashboard/src/components/page-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ const breadcrumbSubstitutions = {
"audit-logs": "Audit Logs",
"admin-settings": "Admin Settings",
"risk-overview": "Risk Overview",
"risk-events": "Risk Events",
"risk-policies": "Risk Policies",
// The URL segments `slack` and `clis` are preserved for backwards
// compatibility, but the sidebar/route titles were rebranded — map them
Expand Down
9 changes: 9 additions & 0 deletions client/dashboard/src/pages/logs/Logs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Page } from "@/components/page-layout";
import { RequireScope } from "@/components/require-scope";
import { LogsTools } from "@/components/observe/LogsTools";
import { ObserveTabNav } from "@/components/observe/ObserveTabNav";
import RiskEvents from "@/pages/security/RiskEvents";

export function LogsRoot() {
return (
Expand Down Expand Up @@ -37,3 +38,11 @@ export function LogsMCPPage() {
</RequireScope>
);
}

export function LogsRiskEventsPage() {
return (
<RequireScope scope="org:admin" level="page">
<RiskEvents />
</RequireScope>
);
}
Loading
Loading