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
10 changes: 5 additions & 5 deletions tests/e2e/features/placeholders/placeholders.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ test.describe('Placeholder and Enterprise Pages', () => {
await expect(page.getByText(/Prompt repository is coming soon/i)).toBeVisible({ timeout: 10000 })
})

test('should load alert-channels page', async ({ page }) => {
await page.goto('/workspace/alert-channels')
test('should load alerting page', async ({ page }) => {
await page.goto('/workspace/alerting')
await page.waitForLoadState('networkidle')
await expect(page.getByText('Unlock alert channels for better observability')).toBeVisible()
const readMore = page.getByRole('button', { name: /Read more/i })
await expect(page.getByTestId('alert-rules-title')).toBeVisible()
const readMore = page.getByTestId('alert-rules-read-more')
await expect(readMore).toBeVisible()
const [popup] = await Promise.all([page.waitForEvent('popup'), readMore.click()])
await expect(popup).toHaveURL(/^https:\/\/docs\.getbifrost\.ai\/enterprise\/alert-channels(\?|$)/)
await expect(popup).toHaveURL(/^https:\/\/docs\.getbifrost\.ai\/enterprise\/alerting\/alert-rules(\?|$)/)
await popup.close()
Comment thread
greptile-apps[bot] marked this conversation as resolved.
})

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import AlertingPlaceholderView from "./alertingPlaceholderView";

export default function AlertChannelsView() {
return (
<AlertingPlaceholderView
title="Unlock alerting channels for proactive monitoring"
description="This feature is a part of the Bifrost enterprise license. Configure Slack, PagerDuty, OpsGenie, and webhook alerts to stay ahead of budget and performance issues."
readmeLink="https://docs.getbifrost.ai/enterprise/alerting/alert-channels"
testIdPrefix="alert-channels"
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import AlertingPlaceholderView from "./alertingPlaceholderView";

export default function AlertHistoryView() {
return (
<AlertingPlaceholderView
title="Unlock alerting history for proactive monitoring"
description="This feature is a part of the Bifrost enterprise license. Review alert delivery outcomes, failures, and resolution events in one place."
readmeLink="https://docs.getbifrost.ai/enterprise/alerting/alert-history"
testIdPrefix="alert-history"
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import AlertingPlaceholderView from "./alertingPlaceholderView";

export default function AlertRulesView() {
return (
<AlertingPlaceholderView
title="Unlock alerting rules for proactive monitoring"
description="This feature is a part of the Bifrost enterprise license. Define alerting rules to catch budget, latency, and degradation issues before they become incidents."
readmeLink="https://docs.getbifrost.ai/enterprise/alerting/alert-rules"
testIdPrefix="alert-rules"
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Bell } from "lucide-react";
import ContactUsView from "../views/contactUsView";

type AlertingPlaceholderViewProps = {
title: string;
description: string;
testIdPrefix: string;
/** Docs page this placeholder links to. Defaults to the alerting overview. */
readmeLink?: string;
};

export default function AlertingPlaceholderView({
title,
description,
testIdPrefix,
readmeLink = "https://docs.getbifrost.ai/enterprise/alerting/overview",
}: AlertingPlaceholderViewProps) {
return (
<div className="h-full w-full">
<ContactUsView
className="mx-auto min-h-[80vh]"
icon={<Bell className="h-[5.5rem] w-[5.5rem]" strokeWidth={1} />}
title={title}
description={description}
readmeLink={readmeLink}
testIdPrefix={testIdPrefix}
/>
</div>
);
}
6 changes: 0 additions & 6 deletions ui/app/workspace/alert-channels/layout.tsx

This file was deleted.

9 changes: 0 additions & 9 deletions ui/app/workspace/alert-channels/page.tsx

This file was deleted.

6 changes: 6 additions & 0 deletions ui/app/workspace/alerting/channels/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { createFileRoute } from "@tanstack/react-router";
import AlertChannelsPage from "./page";

export const Route = createFileRoute("/workspace/alerting/channels")({
component: AlertChannelsPage,
});
9 changes: 9 additions & 0 deletions ui/app/workspace/alerting/channels/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import AlertChannelsView from "@enterprise/components/alerting/alertChannelsView";

export default function AlertChannelsPage() {
return (
<div className="mx-auto w-full max-w-7xl">
<AlertChannelsView />
</div>
);
}
6 changes: 6 additions & 0 deletions ui/app/workspace/alerting/history/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { createFileRoute } from "@tanstack/react-router";
import AlertHistoryPage from "./page";

export const Route = createFileRoute("/workspace/alerting/history")({
component: AlertHistoryPage,
});
9 changes: 9 additions & 0 deletions ui/app/workspace/alerting/history/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import AlertHistoryView from "@enterprise/components/alerting/alertHistoryView";

export default function AlertHistoryPage() {
return (
<div className="mx-auto w-full max-w-7xl">
<AlertHistoryView />
</div>
);
}
20 changes: 20 additions & 0 deletions ui/app/workspace/alerting/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { createFileRoute, Outlet, redirect } from "@tanstack/react-router";
import { NoPermissionView } from "@/components/noPermissionView";
import { RbacOperation, RbacResource, useRbac } from "@enterprise/lib";

function RouteComponent() {
const hasAlertingAccess = useRbac(RbacResource.Governance, RbacOperation.View);
if (!hasAlertingAccess) {
return <NoPermissionView entity="alerting" />;
}
return <Outlet />;
}

export const Route = createFileRoute("/workspace/alerting")({
beforeLoad: ({ location }) => {
if (location.pathname === "/workspace/alerting" || location.pathname === "/workspace/alerting/") {
throw redirect({ to: "/workspace/alerting/rules", replace: true });
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.
},
component: RouteComponent,
});
6 changes: 6 additions & 0 deletions ui/app/workspace/alerting/rules/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { createFileRoute } from "@tanstack/react-router";
import AlertRulesPage from "./page";

export const Route = createFileRoute("/workspace/alerting/rules")({
component: AlertRulesPage,
});
9 changes: 9 additions & 0 deletions ui/app/workspace/alerting/rules/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import AlertRulesView from "@enterprise/components/alerting/alertRulesView";

export default function AlertRulesPage() {
return (
<div className="mx-auto w-full max-w-7xl">
<AlertRulesView />
</div>
);
}
58 changes: 55 additions & 3 deletions ui/components/sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
ArrowUpRight,
Bell,
BookOpenText,
BookUser,
Boxes,
Expand All @@ -15,12 +16,15 @@ import {
ShieldHalf,
FlaskConical,
FolderGit,
Gavel,
Globe,
History,
KeyRound,
Landmark,
LayoutGrid,
LogOut,
Logs,
Megaphone,
Network,
PanelLeftClose,
PanelLeftOpen,
Expand Down Expand Up @@ -321,7 +325,12 @@ const SidebarItemView = ({
let menuButton: React.ReactNode;
if (hasSubItems) {
menuButton = (
<SidebarMenuButton tooltip={isSidebarCollapsed ? undefined : item.title} className={buttonClassName} onClick={handleClick}>
<SidebarMenuButton
tooltip={isSidebarCollapsed ? undefined : item.title}
className={buttonClassName}
onClick={handleClick}
data-testid={`sidebar-item-btn-${slug(item.title)}`}
>
{innerContent}
</SidebarMenuButton>
);
Expand Down Expand Up @@ -464,12 +473,21 @@ const SidebarItemView = ({
return (
<SidebarMenuSubItem key={subItem.title}>
{subItem.hasAccess === false ? (
<SidebarMenuSubButton data-nav-url={subItemHref} className={subItemClassName}>
<SidebarMenuSubButton
data-nav-url={subItemHref}
data-testid={`sidebar-subitem-disabled-${slug(subItem.title)}`}
className={subItemClassName}
>
{subInner}
</SidebarMenuSubButton>
) : (
<SidebarMenuSubButton asChild className={subItemClassName}>
<Link to={subItemHref as any} preload="intent" data-nav-url={subItemHref}>
<Link
to={subItemHref as any}
preload="intent"
data-nav-url={subItemHref}
data-testid={`sidebar-subitem-link-${slug(subItem.title)}`}
>
{subInner}
</Link>
</SidebarMenuSubButton>
Expand Down Expand Up @@ -543,6 +561,9 @@ export default function AppSidebar() {
});
const hasLogsAccess = useRbac(RbacResource.Logs, RbacOperation.View);
const hasObservabilityAccess = useRbac(RbacResource.Observability, RbacOperation.View);
// Alerting is currently surfaced under the existing governance permission
// until enterprise alerting gets its own RBAC resource.
const hasAlertingAccess = useRbac(RbacResource.Governance, RbacOperation.View);
const hasDashboardAccess = useRbac(RbacResource.Dashboard, RbacOperation.View);
const hasModelProvidersAccess = useRbac(RbacResource.ModelProvider, RbacOperation.View);
const hasMCPGatewayAccess = useRbac(RbacResource.MCPGateway, RbacOperation.View);
Expand Down Expand Up @@ -752,6 +773,36 @@ export default function AppSidebar() {
description: "Manage custom plugins",
hasAccess: hasPluginsAccess,
},
{
title: "Alerting",
url: "/workspace/alerting",
icon: Bell,
description: "Manage alert channels, rules, and history",
hasAccess: hasAlertingAccess,
subItems: [
{
title: "Channels",
url: "/workspace/alerting/channels",
icon: Megaphone,
description: "Configure notification channels",
hasAccess: hasAlertingAccess,
},
{
title: "Rules",
url: "/workspace/alerting/rules",
icon: Gavel,
description: "Define alerting rules",
hasAccess: hasAlertingAccess,
},
{
title: "History",
url: "/workspace/alerting/history",
icon: History,
description: "Review alert delivery history",
hasAccess: hasAlertingAccess,
},
],
},
{
title: "Governance",
url: "/workspace/governance",
Expand Down Expand Up @@ -977,6 +1028,7 @@ export default function AppSidebar() {
hasLogsAccess,
hasAPIKeyAccess,
hasObservabilityAccess,
hasAlertingAccess,
hasDashboardAccess,
hasModelProvidersAccess,
hasMCPGatewayAccess,
Expand Down
Loading