diff --git a/src/App.test.tsx b/src/App.test.tsx index 298c77b1..8593b0c7 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -61,7 +61,7 @@ describe("App", () => { render(); await waitFor(() => expect( - screen.getByRole("link", { name: /codeGate dashboard/i }), + screen.getByRole("link", { name: "CodeGate Dashboard" }), ).toBeVisible(), ); expect(screen.getByRole("link", { name: "Dashboard" })).toBeVisible(); diff --git a/src/components/Header.tsx b/src/components/Header.tsx index b4bc5743..2f28839c 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -2,6 +2,7 @@ import { Link } from "react-router-dom"; import { SidebarTrigger } from "./ui/sidebar"; import { HoverPopover } from "./HoverPopover"; import { Separator, ButtonDarkMode } from "@stacklok/ui-kit"; +import { WorkspacesSelection } from "@/features/workspace/components/workspaces-selection"; export function Header({ hasError }: { hasError?: boolean }) { return ( @@ -9,21 +10,23 @@ export function Header({ hasError }: { hasError?: boolean }) { aria-label="App header" className="shrink-0 h-16 px-3 items-center flex w-full bg-gray-25 border-b-gray-200 border-b" > -
+
{!hasError && ( <> - + )} -
diff --git a/src/features/workspace/components/workspaces-selection.tsx b/src/features/workspace/components/workspaces-selection.tsx new file mode 100644 index 00000000..010eac1e --- /dev/null +++ b/src/features/workspace/components/workspaces-selection.tsx @@ -0,0 +1,92 @@ +import { useWorkspacesData } from "@/hooks/useWorkspacesData"; +import { + Button, + DialogTrigger, + Input, + ListBox, + ListBoxItem, + Popover, + SearchField, + Separator, +} from "@stacklok/ui-kit"; +import { useQueryClient } from "@tanstack/react-query"; +import clsx from "clsx"; +import { ChevronDown, Search, Settings } from "lucide-react"; +import { useState } from "react"; +import { Link } from "react-router-dom"; + +export function WorkspacesSelection() { + const queryClient = useQueryClient(); + const { data } = useWorkspacesData(); + const [isOpen, setIsOpen] = useState(false); + const [searchWorkspace, setSearchWorkspace] = useState(""); + const workspaces = data?.workspaces ?? []; + const filteredWorkspaces = workspaces.filter((workspace) => + workspace.name.toLowerCase().includes(searchWorkspace.toLowerCase()), + ); + const activeWorkspace = workspaces.find((workspace) => workspace.is_active); + + const handleWorkspaceClick = () => { + queryClient.invalidateQueries({ refetchType: "all" }); + setIsOpen(false); + }; + + return ( + setIsOpen(test)}> + + + +
+
+ + } /> + +
+ + ( +

No workspaces found

+ )} + > + {(item) => ( + handleWorkspaceClick()} + className={clsx( + "cursor-pointer py-2 m-1 text-base hover:bg-gray-300", + { + "bg-gray-900 text-white hover:text-secondary": + item.is_active, + }, + )} + key={item.name} + > + {item.name} + + )} +
+ + setIsOpen(false)} + className="text-secondary pt-3 px-2 gap-2 flex" + > + + Manage Workspaces + +
+
+
+ ); +} diff --git a/src/hooks/useSse.ts b/src/hooks/useSse.ts index 9ee84483..baf753fc 100644 --- a/src/hooks/useSse.ts +++ b/src/hooks/useSse.ts @@ -12,12 +12,12 @@ export function useSse() { useEffect(() => { const eventSource = new EventSource( - `${BASE_URL}/dashboard/alerts_notification`, + `${BASE_URL}/api/v1/dashboard/alerts_notification`, ); eventSource.onmessage = function (event) { - queryClient.invalidateQueries({ refetchType: "all" }); if (event.data.toLowerCase().includes("new alert detected")) { + queryClient.invalidateQueries({ refetchType: "all" }); sendNotification("CodeGate Dashboard", { body: "New Alert detected!", }); diff --git a/src/mocks/msw/fixtures/GET_WORKSPACES.json b/src/mocks/msw/fixtures/GET_WORKSPACES.json new file mode 100644 index 00000000..e7669999 --- /dev/null +++ b/src/mocks/msw/fixtures/GET_WORKSPACES.json @@ -0,0 +1,10 @@ +[ + { + "name": "workspace-1", + "is_active": true + }, + { + "name": "workspace-2", + "is_active": false + } +] \ No newline at end of file diff --git a/src/mocks/msw/handlers.ts b/src/mocks/msw/handlers.ts index 46e62cd6..f202e94f 100644 --- a/src/mocks/msw/handlers.ts +++ b/src/mocks/msw/handlers.ts @@ -1,6 +1,7 @@ import { http, HttpResponse } from "msw"; import mockedPrompts from "@/mocks/msw/fixtures/GET_MESSAGES.json"; import mockedAlerts from "@/mocks/msw/fixtures/GET_ALERTS.json"; +import mockedWorkspaces from "@/mocks/msw/fixtures/GET_WORKSPACES.json"; export const handlers = [ http.get("*/health", () => @@ -20,4 +21,7 @@ export const handlers = [ http.get("*/dashboard/alerts", () => { return HttpResponse.json(mockedAlerts); }), + http.get("*/workspaces", () => { + return HttpResponse.json(mockedWorkspaces); + }), ];