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
28 changes: 23 additions & 5 deletions src/components/admin/tabs/OverviewTab.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";

import { appFetch, withBasePath } from "@/lib/config/base-path";
import { adminSectionPath } from "@/lib/admin-sections";
import { useEffect, useMemo, useState, useCallback } from "react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
Expand Down Expand Up @@ -999,12 +1000,29 @@ function AnalyticsSection({

{/* Recent Activity Feed */}
<div className="rounded-xl border border-hairline bg-panel p-5">
<h3 className="text-sm font-bold text-fg-secondary mb-4 flex items-center gap-2">
<Clock className="h-4 w-4 text-brand" />
Recent Activity
</h3>
<div className="mb-4 flex items-center justify-between gap-2">
<h3 className="text-sm font-bold text-fg-secondary flex items-center gap-2">
<Clock className="h-4 w-4 text-brand" />
Recent Activity
</h3>
{/* Unconditional: the preview cannot show proxy-recorded boundary denials, so the
Audit tab's scope disclosure must stay one click away in every feed state. */}
<Link
href={adminSectionPath("audit")}
data-testid="overview-audit-link"
className="inline-flex items-center gap-1 text-xs text-fg-subtle hover:text-fg-tertiary transition-colors"
>
Audit
<ArrowRight className="h-3 w-3" aria-hidden="true" />
</Link>
</div>
{activityFeed.length === 0 ? (
<div className="flex items-center justify-center py-8 text-sm text-fg-subtle">No recent activity.</div>
<div
data-testid="overview-recent-activity-empty"
className="flex items-center justify-center py-8 text-sm text-fg-subtle"
>
No recent activity here. The Audit tab explains what this feed does not capture.
</div>
) : (
<div className="max-h-[260px] overflow-y-auto editor-scrollbar space-y-1">
<AnimatePresence>
Expand Down
27 changes: 26 additions & 1 deletion tests/components/admin/OverviewTab.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ import React from "react";
import { mockGlobalFetch, restoreGlobalFetch } from "../../helpers/mock-fetch";

import { OverviewTab, DB_TYPES_PREVIEW } from "@/components/admin/tabs/OverviewTab";
import { adminSectionPath } from "@/lib/admin-sections";
import { EXTERNAL_DATABASE_TYPES } from "@/lib/db/compatibility";
import { getDBConfig } from "@/lib/db-ui-config";

Expand Down Expand Up @@ -393,7 +394,7 @@ describe("OverviewTab", () => {
await act(async () => {
renderResult = render(<OverviewTab user={{ username: "admin", role: "admin" }} />);
});
const { queryByText } = renderResult!;
const { queryByText, queryByTestId } = renderResult!;

await waitFor(() => {
// Audit events are mapped into the feed alongside query history
Expand All @@ -403,6 +404,30 @@ describe("OverviewTab", () => {
expect(queryByText("5m ago")).not.toBeNull();
expect(queryByText("5h ago")).not.toBeNull();
expect(queryByText("3d ago")).not.toBeNull();
const auditLink = queryByTestId("overview-audit-link");
expect(auditLink).not.toBeNull();
expect(auditLink?.getAttribute("href")).toBe(adminSectionPath("audit"));
});
});

test("Recent Activity uses an honest empty state and still links to Audit", async () => {
mockGetHistory.mockImplementation(() => []);

let renderResult: ReturnType<typeof render>;
await act(async () => {
renderResult = render(<OverviewTab user={{ username: "admin", role: "admin" }} />);
});
const { queryByText, queryByTestId } = renderResult!;

await waitFor(() => {
const auditLink = queryByTestId("overview-audit-link");
expect(auditLink).not.toBeNull();
expect(auditLink?.getAttribute("href")).toBe(adminSectionPath("audit"));
expect(queryByTestId("overview-recent-activity-empty")).not.toBeNull();
expect(
queryByText("No recent activity here. The Audit tab explains what this feed does not capture."),
).not.toBeNull();
expect(queryByText("No recent activity.")).toBeNull();
});
});

Expand Down
Loading