+
{title}
diff --git a/src/features/dashboard-messages/components/__tests__/table-messages.empty-state.test.tsx b/src/features/dashboard-messages/components/__tests__/table-messages.empty-state.test.tsx
index c48c30a1..27b7d0c2 100644
--- a/src/features/dashboard-messages/components/__tests__/table-messages.empty-state.test.tsx
+++ b/src/features/dashboard-messages/components/__tests__/table-messages.empty-state.test.tsx
@@ -6,9 +6,9 @@ import { useSearchParams } from "react-router-dom";
import { delay, http, HttpHandler, HttpResponse } from "msw";
import { mockAlert } from "../../../../mocks/msw/mockers/alert.mock";
import { AlertsFilterView } from "../../hooks/use-messages-filter-search-params";
-import { TableMessages } from "../table-messages";
import { hrefs } from "@/lib/hrefs";
import { mswEndpoint } from "@/test/msw-endpoint";
+import { TableMessagesEmptyState } from "../table-messages-empty-state";
enum IllustrationTestId {
ALERT = "illustration-alert",
@@ -48,7 +48,7 @@ type TestCase = {
vi.mock("react-router-dom", async () => {
const original =
await vi.importActual
(
- "react-router-dom"
+ "react-router-dom",
);
return {
...original,
@@ -59,7 +59,7 @@ vi.mock("react-router-dom", async () => {
vi.mock("@stacklok/ui-kit", async () => {
const original =
await vi.importActual(
- "@stacklok/ui-kit"
+ "@stacklok/ui-kit",
);
return {
...original,
@@ -116,7 +116,7 @@ const TEST_CASES: TestCase[] = [
mswEndpoint("/api/v1/workspaces/:workspace_name/messages"),
() => {
return HttpResponse.json([]);
- }
+ },
),
],
searchParams: {
@@ -158,9 +158,9 @@ const TEST_CASES: TestCase[] = [
mswEndpoint("/api/v1/workspaces/:workspace_name/messages"),
() => {
return HttpResponse.json(
- Array.from({ length: 10 }, () => mockAlert({ type: "malicious" }))
+ Array.from({ length: 10 }, () => mockAlert({ type: "malicious" })),
);
- }
+ },
),
],
searchParams: { search: "foo-bar", view: AlertsFilterView.ALL },
@@ -202,7 +202,7 @@ const TEST_CASES: TestCase[] = [
mswEndpoint("/api/v1/workspaces/:workspace_name/messages"),
() => {
return HttpResponse.json([]);
- }
+ },
),
],
searchParams: {
@@ -248,9 +248,9 @@ const TEST_CASES: TestCase[] = [
mswEndpoint("/api/v1/workspaces/:workspace_name/messages"),
() => {
return HttpResponse.json(
- Array.from({ length: 10 }).map(() => mockAlert({ type: "secret" }))
+ Array.from({ length: 10 }).map(() => mockAlert({ type: "secret" })),
);
- }
+ },
),
],
searchParams: {
@@ -291,10 +291,10 @@ const TEST_CASES: TestCase[] = [
() => {
return HttpResponse.json(
Array.from({ length: 10 }).map(() =>
- mockAlert({ type: "malicious" })
- )
+ mockAlert({ type: "malicious" }),
+ ),
);
- }
+ },
),
],
searchParams: {
@@ -321,11 +321,13 @@ test.each(TEST_CASES)("$testDescription", async (testCase) => {
() => {},
]);
- const { getByText, getByRole, getByTestId } = render();
+ const { getByText, getByRole, getByTestId } = render(
+ ,
+ );
await waitFor(() => {
expect(
- getByRole("heading", { level: 4, name: testCase.expected.title })
+ getByRole("heading", { level: 4, name: testCase.expected.title }),
).toBeVisible();
expect(getByText(testCase.expected.body)).toBeVisible();
expect(getByTestId(testCase.expected.illustrationTestId)).toBeVisible();
diff --git a/src/features/dashboard-messages/components/table-messages-empty-state.tsx b/src/features/dashboard-messages/components/table-messages-empty-state.tsx
index 9f97b2c7..0461b54b 100644
--- a/src/features/dashboard-messages/components/table-messages-empty-state.tsx
+++ b/src/features/dashboard-messages/components/table-messages-empty-state.tsx
@@ -20,13 +20,16 @@ import {
} from "../hooks/use-messages-filter-search-params";
import { match, P } from "ts-pattern";
import { useQueryGetWorkspaceMessages } from "@/hooks/use-query-get-workspace-messages";
+import { twMerge } from "tailwind-merge";
function EmptyStateLoading() {
return (
(
+
+ )}
actions={null}
/>
);
@@ -117,7 +120,7 @@ function EmptyStateSecrets() {
);
}
-function EmptyStateError() {
+export function EmptyStateError() {
return (
,
- )
.with(
{
hasWorkspaceMessages: false,
hasMultipleWorkspaces: false,
- search: P._,
- view: P._,
+ search: P.any,
+ view: P.any,
+ isLoading: false,
},
() => ,
)
@@ -200,7 +194,8 @@ export function TableAlertsEmptyState() {
hasWorkspaceMessages: true,
hasMultipleWorkspaces: P.any,
search: P.string.select(),
- view: P._,
+ view: P.any,
+ isLoading: false,
},
(search) => ,
)
@@ -208,8 +203,9 @@ export function TableAlertsEmptyState() {
{
hasWorkspaceMessages: false,
hasMultipleWorkspaces: P.any,
- search: P._,
+ search: P.any,
view: P.any,
+ isLoading: false,
},
() => ,
)
@@ -217,8 +213,9 @@ export function TableAlertsEmptyState() {
{
hasWorkspaceMessages: true,
hasMultipleWorkspaces: P.any,
- search: P._,
+ search: P.any,
view: AlertsFilterView.MALICIOUS,
+ isLoading: false,
},
() => ,
)
@@ -227,8 +224,9 @@ export function TableAlertsEmptyState() {
hasWorkspaceMessages: true,
hasMultipleWorkspaces: P.any,
view: AlertsFilterView.SECRETS,
+ isLoading: false,
},
() => ,
)
- .otherwise(() => );
+ .otherwise(() => );
}
diff --git a/src/features/dashboard-messages/components/table-messages.tsx b/src/features/dashboard-messages/components/table-messages.tsx
index 6d5e63d4..166238e0 100644
--- a/src/features/dashboard-messages/components/table-messages.tsx
+++ b/src/features/dashboard-messages/components/table-messages.tsx
@@ -17,7 +17,10 @@ import { TableAlertTokenUsage } from "./table-alert-token-usage";
import { useMessagesFilterSearchParams } from "../hooks/use-messages-filter-search-params";
import { Key01, PackageX } from "@untitled-ui/icons-react";
-import { TableAlertsEmptyState } from "./table-messages-empty-state";
+import {
+ EmptyStateError,
+ TableMessagesEmptyState,
+} from "./table-messages-empty-state";
import { hrefs } from "@/lib/hrefs";
import { isAlertMalicious } from "../../../lib/is-alert-malicious";
import { isAlertSecret } from "../../../lib/is-alert-secret";
@@ -145,7 +148,7 @@ function CellRenderer({
export function TableMessages() {
const { state, prevPage, nextPage } = useMessagesFilterSearchParams();
- const { data = [] } = useQueryGetWorkspaceMessagesTable();
+ const { data = [], isError } = useQueryGetWorkspaceMessagesTable();
const { dataView, hasNextPage, hasPreviousPage } = useClientSidePagination(
data,
state.page,
@@ -160,7 +163,11 @@ export function TableMessages() {
{(column) => }
}
+ renderEmptyState={() => {
+ if (isError) return ;
+
+ return ;
+ }}
items={dataView}
>
{(row) => (
diff --git a/src/features/header/components/header-active-workspace-selector.tsx b/src/features/header/components/header-active-workspace-selector.tsx
index ea204d6e..89618312 100644
--- a/src/features/header/components/header-active-workspace-selector.tsx
+++ b/src/features/header/components/header-active-workspace-selector.tsx
@@ -19,10 +19,17 @@ import { hrefs } from "@/lib/hrefs";
import { twMerge } from "tailwind-merge";
import ChevronDown from "@untitled-ui/icons-react/build/cjs/ChevronDown";
import { SearchMd, Settings01 } from "@untitled-ui/icons-react";
+import { useLocation, useNavigate } from "react-router-dom";
+
+const ROUTES_REQUIRING_REDIRECT = [/^\/$/, /^\/prompt\/(.*)$/];
export function HeaderActiveWorkspaceSelector() {
const queryClient = useQueryClient();
+ const navigate = useNavigate();
+ const location = useLocation();
+ const { pathname } = location;
+
const { data: workspacesResponse } = useQueryListWorkspaces();
const { mutateAsync: activateWorkspace } = useMutationActivateWorkspace();
@@ -32,13 +39,16 @@ export function HeaderActiveWorkspaceSelector() {
const [searchWorkspace, setSearchWorkspace] = useState("");
const workspaces = workspacesResponse?.workspaces ?? [];
const filteredWorkspaces = workspaces.filter((workspace) =>
- workspace.name.toLowerCase().includes(searchWorkspace.toLowerCase()),
+ workspace.name.toLowerCase().includes(searchWorkspace.toLowerCase())
);
const handleWorkspaceClick = (name: string) => {
activateWorkspace({ body: { name } }).then(() => {
// eslint-disable-next-line no-restricted-syntax
queryClient.invalidateQueries({ refetchType: "all" }); // Global setting, refetch **everything**
+ if (ROUTES_REQUIRING_REDIRECT.some((route) => route.test(pathname))) {
+ navigate("/");
+ }
setIsOpen(false);
});
};
@@ -86,7 +96,7 @@ export function HeaderActiveWorkspaceSelector() {
{
"!bg-gray-900 hover:bg-gray-900 !text-gray-25 hover:!text-gray-25":
item.is_active,
- },
+ }
)}
>
{item.name}
@@ -100,12 +110,12 @@ export function HeaderActiveWorkspaceSelector() {
"ml-auto size-6 group-hover/selector:opacity-100 opacity-0 transition-opacity",
item.is_active
? "hover:bg-gray-800 pressed:bg-gray-700"
- : "hover:bg-gray-50 hover:text-primary",
+ : "hover:bg-gray-50 hover:text-primary"
)}
>