mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-11 05:11:49 +01:00
Add join request badge to access sidebar
This commit is contained in:
1 parent
b42796490b
commit
0fe666b72e
2 files changed
+63
-3
No files matched your search
@@ -2,10 +2,14 @@
|
||||
|
||||
import { act } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { CompanySettingsSidebar } from "./CompanySettingsSidebar";
|
||||
|
||||
const sidebarNavItemMock = vi.hoisted(() => vi.fn());
|
||||
const mockSidebarBadgesApi = vi.hoisted(() => ({
|
||||
get: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/router", () => ({
|
||||
Link: ({
|
||||
@@ -25,6 +29,7 @@ vi.mock("@/lib/router", () => ({
|
||||
|
||||
vi.mock("@/context/CompanyContext", () => ({
|
||||
useCompany: () => ({
|
||||
selectedCompanyId: "company-1",
|
||||
selectedCompany: { id: "company-1", name: "Paperclip" },
|
||||
}),
|
||||
}));
|
||||
@@ -41,21 +46,39 @@ vi.mock("./SidebarNavItem", () => ({
|
||||
to: string;
|
||||
label: string;
|
||||
end?: boolean;
|
||||
badge?: number;
|
||||
}) => {
|
||||
sidebarNavItemMock(props);
|
||||
return <div>{props.label}</div>;
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("@/api/sidebarBadges", () => ({
|
||||
sidebarBadgesApi: mockSidebarBadgesApi,
|
||||
}));
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;
|
||||
|
||||
async function flushReact() {
|
||||
await act(async () => {
|
||||
await Promise.resolve();
|
||||
await new Promise((resolve) => window.setTimeout(resolve, 0));
|
||||
});
|
||||
}
|
||||
|
||||
describe("CompanySettingsSidebar", () => {
|
||||
let container: HTMLDivElement;
|
||||
|
||||
beforeEach(() => {
|
||||
container = document.createElement("div");
|
||||
document.body.appendChild(container);
|
||||
mockSidebarBadgesApi.get.mockResolvedValue({
|
||||
inbox: 0,
|
||||
approvals: 0,
|
||||
failedRuns: 0,
|
||||
joinRequests: 2,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -66,10 +89,18 @@ describe("CompanySettingsSidebar", () => {
|
||||
|
||||
it("renders the company back link and the settings sections in the sidebar", async () => {
|
||||
const root = createRoot(container);
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: { queries: { retry: false } },
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
root.render(<CompanySettingsSidebar />);
|
||||
root.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<CompanySettingsSidebar />
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
});
|
||||
await flushReact();
|
||||
|
||||
expect(container.textContent).toContain("Paperclip");
|
||||
expect(container.textContent).toContain("Company Settings");
|
||||
@@ -87,6 +118,7 @@ describe("CompanySettingsSidebar", () => {
|
||||
expect.objectContaining({
|
||||
to: "/company/settings/access",
|
||||
label: "Access",
|
||||
badge: 2,
|
||||
end: true,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -1,12 +1,34 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { ChevronLeft, MailPlus, Settings, Shield, SlidersHorizontal } from "lucide-react";
|
||||
import { sidebarBadgesApi } from "@/api/sidebarBadges";
|
||||
import { ApiError } from "@/api/client";
|
||||
import { Link } from "@/lib/router";
|
||||
import { queryKeys } from "@/lib/queryKeys";
|
||||
import { useCompany } from "@/context/CompanyContext";
|
||||
import { useSidebar } from "@/context/SidebarContext";
|
||||
import { SidebarNavItem } from "./SidebarNavItem";
|
||||
|
||||
export function CompanySettingsSidebar() {
|
||||
const { selectedCompany } = useCompany();
|
||||
const { selectedCompany, selectedCompanyId } = useCompany();
|
||||
const { isMobile, setSidebarOpen } = useSidebar();
|
||||
const { data: badges } = useQuery({
|
||||
queryKey: selectedCompanyId
|
||||
? queryKeys.sidebarBadges(selectedCompanyId)
|
||||
: ["sidebar-badges", "__disabled__"] as const,
|
||||
queryFn: async () => {
|
||||
try {
|
||||
return await sidebarBadgesApi.get(selectedCompanyId!);
|
||||
} catch (error) {
|
||||
if (error instanceof ApiError && (error.status === 401 || error.status === 403)) {
|
||||
return null;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
enabled: !!selectedCompanyId,
|
||||
retry: false,
|
||||
refetchInterval: 15_000,
|
||||
});
|
||||
|
||||
return (
|
||||
<aside className="w-60 h-full min-h-0 border-r border-border bg-background flex flex-col">
|
||||
@@ -32,7 +54,13 @@ export function CompanySettingsSidebar() {
|
||||
<nav className="flex-1 min-h-0 overflow-y-auto scrollbar-auto-hide px-3 py-2">
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<SidebarNavItem to="/company/settings" label="General" icon={SlidersHorizontal} end />
|
||||
<SidebarNavItem to="/company/settings/access" label="Access" icon={Shield} end />
|
||||
<SidebarNavItem
|
||||
to="/company/settings/access"
|
||||
label="Access"
|
||||
icon={Shield}
|
||||
badge={badges?.joinRequests ?? 0}
|
||||
end
|
||||
/>
|
||||
<SidebarNavItem to="/company/settings/invites" label="Invites" icon={MailPlus} end />
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
Reference in new issue
Block a user