ronantakizawa/codereview-bench huggingface dataset:
Repository: All-Hands-AI/OpenHands
File: frontend/src/components/features/chat/chat-status-indicator.tsx (TypeScript)

Code before:
import { useEffect, useState } from "react";
import DebugStackframeDot from "#/icons/debug-stackframe-dot.svg?react";

interface ChatStatusIndicatorProps {
  status: string;
  statusColor: string;
}

function ChatStatusIndicator({
  status,
  statusColor,
}: ChatStatusIndicatorProps) {
  const [displayStatus, setDisplayStatus] = useState(status);
  const [fadingOut, setFadingOut] = useState(false);

  useEffect(() => {
    setFadingOut(true);

    const timeout = setTimeout(() => {
      setDisplayStatus(status);
      setFadingOut(false);
    }, 300);

    return () => clearTimeout(timeout);
  }, [status, displayStatus]);

  return (
    <div
      data-testid="chat-status-indicator"
      className="h-[31px] w-fit rounded-[100px] pt-[20px] pr-[16px] pb-[20px] pl-[5px] bg-[#25272D] flex items-center mb-[8px]"
    >
      <span className="animate-[pulse_1.2s_ease-in-out_infinite]">
        <DebugStackframeDot className="w-6 h-6 shrink-0" color={statusColor} />
      </span>
      <span
        className={`font-normal text-[11px] leading-[20px] normal-case transition-opacity duration-900 ease-out ${fadingOut ? "opacity-0" : "opacity-100"}`}
      >
        {displayStatus}
      </span>
    </div>
  );
}

export default ChatStatusIndicator;


Review comment (suggestion):
Though non-blocking for this PR, we have an animation library called `framer-motion` (now known as just `motion`) and they have an `AnimatePresence` component that can do the same without managing our own state:

https://motion.dev/docs/react-animate-presence

Code after:
import { cn } from "@heroui/react";
import { motion, AnimatePresence } from "framer-motion";
import DebugStackframeDot from "#/icons/debug-stackframe-dot.svg?react";

interface ChatStatusIndicatorProps {
  status: string;
  statusColor: string;
}

function ChatStatusIndicator({
  status,
  statusColor,
}: ChatStatusIndicatorProps) {
  return (
    <div
      data-testid="chat-status-indicator"
      className={cn(
        "h-[31px] w-fit rounded-[100px] pt-[20px] pr-[16px] pb-[20px] pl-[5px] bg-[#25272D] flex items-center gap-2",
      )}
    >
      <AnimatePresence mode="wait">
        {/* Dot */}
        <motion.span
          key={`dot-${status}`}
          className="animate-[pulse_1.2s_ease-in-out_infinite]"
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          exit={{ opacity: 0 }}
          transition={{ duration: 0.3 }}
        >
          <DebugStackframeDot
            className="w-6 h-6 shrink-0"
            color={statusColor}
          />
        </motion.span>

        {/* Text */}
        <motion.span
          key={`text-${status}`}
          initial={{ opacity: 0, y: -2 }}
          animate={{ opacity: 1, y: 0 }}
          exit={{ opacity: 0, y: 2 }}
          transition={{ duration: 0.3 }}
          className="font-normal text-[11px] leading-[20px] normal-case"
        >
          {status}
        </motion.span>
      </AnimatePresence>
    </div>
  );
Repository: All-Hands-AI/OpenHands
File: frontend/src/components/features/org/organization-member-list-item.tsx (TypeScript)

Code before:
      <div className="flex items-center gap-2">
        <span
          className={cn(
            "text-sm font-semibold",
            status === "invited" && "text-gray-400",
          )}
        >
          {email}
        </span>

        {status === "invited" && (
          <span className="text-xs text-tertiary-light border border-tertiary px-2 py-1 rounded-lg">
            {t(I18nKey.ORG$STATUS_INVITED)}
          </span>
        )}
      </div>

      <div className="relative">
        <span
          onClick={handleRoleClick}
          className={cn(
            "text-xs text-gray-400 flex items-center gap-1 capitalize",
            roleSelectionIsPermitted ? "cursor-pointer" : "cursor-not-allowed",
          )}
        >
          {ROLE_LABELS[role]}
          {hasPermissionToChangeRole && <ChevronDown size={14} />}
        </span>

        {roleSelectionIsPermitted && contextMenuOpen && (
          <OrganizationMemberRoleContextMenu
            onClose={() => setContextMenuOpen(false)}
            onRoleChange={onRoleChange}
            onRemove={onRemove}
            availableRolesToChangeTo={availableRolesToChangeTo}
          />
        )}
      </div>
    </div>
  );
}


Review comment (suggestion):
Please revert, if your objective was to render capitalized text, please look at the `capitalize` class https://tailwindcss.com/docs/text-transform


```suggestion
          {role}
```

Code after:
        >
          {email}
        </span>

        {status === "invited" && (
          <span className="text-xs text-tertiary-light border border-tertiary px-2 py-1 rounded-lg">
            {t(I18nKey.ORG$STATUS_INVITED)}
          </span>
        )}
      </div>

      <div className="relative">
        <span
          onClick={handleRoleClick}
          className={cn(
            "text-xs text-gray-400 flex items-center gap-1 capitalize",
            roleSelectionIsPermitted ? "cursor-pointer" : "cursor-not-allowed",
          )}
        >
          {role}
          {hasPermissionToChangeRole && <ChevronDown size={14} />}
        </span>

        {roleSelectionIsPermitted && contextMenuOpen && (
          <OrganizationMemberRoleContextMenu
            onClose={() => setContextMenuOpen(false)}
            onRoleChange={onRoleChange}
            onRemove={onRemove}
            availableRolesToChangeTo={availableRolesToChangeTo}
          />
        )}
      </div>
    </div>
  );
}
Repository: All-Hands-AI/OpenHands
File: frontend/src/components/features/org/organization-member-role-context-menu.tsx (TypeScript)

Code before:
          testId="admin-option"
          onClick={(event) => handleRoleChangeClick(event, "admin")}
          className={contextMenuListItemClassName}
        >
          <ContextMenuIconText
            icon={
              <AdminIcon
                width={16}
                height={16}
                className="text-white pl-[2px]"
              />
            }
            text={ROLE_LABELS.admin}
            className="capitalize"
          />
        </ContextMenuListItem>
      )}
      {availableRolesToChangeTo.includes("member") && (
        <ContextMenuListItem
          testId="member-option"
          onClick={(event) => handleRoleChangeClick(event, "member")}
          className={contextMenuListItemClassName}
        >
          <ContextMenuIconText
            icon={<UserIcon width={16} height={16} className="text-white" />}
            text={ROLE_LABELS.member}
            className="capitalize"
          />
        </ContextMenuListItem>
      )}
      <ContextMenuListItem
        testId="remove-option"
        onClick={handleRemoveClick}
        className={contextMenuListItemClassName}
      >
        <ContextMenuIconText
          icon={<DeleteIcon width={16} height={16} className="text-red-500" />}
          text={t(I18nKey.ORG$REMOVE)}
          className="text-red-500 capitalize"
        />
      </ContextMenuListItem>
    </ContextMenu>
  );
}


Review comment (suggestion):
```suggestion
            text={t(I18nKey.ORG$ROLE_USER)}
```

Code after:
              <AdminIcon
                width={16}
                height={16}
                className="text-white pl-[2px]"
              />
            }
            text={t(I18nKey.ORG$ROLE_ADMIN)}
            className="capitalize"
          />
        </ContextMenuListItem>
      )}
      {availableRolesToChangeTo.includes("member") && (
        <ContextMenuListItem
          testId="member-option"
          onClick={(event) => handleRoleChangeClick(event, "member")}
          className={contextMenuListItemClassName}
        >
          <ContextMenuIconText
            icon={<UserIcon width={16} height={16} className="text-white" />}
            text={t(I18nKey.ORG$ROLE_MEMBER)}
            className="capitalize"
          />
        </ContextMenuListItem>
      )}
      <ContextMenuListItem
        testId="remove-option"
        onClick={handleRemoveClick}
        className={contextMenuListItemClassName}
      >
        <ContextMenuIconText
          icon={<DeleteIcon width={16} height={16} className="text-red-500" />}
          text={t(I18nKey.ORG$REMOVE)}
          className="text-red-500 capitalize"
        />
      </ContextMenuListItem>
    </ContextMenu>
  );
}
Repository: All-Hands-AI/OpenHands
File: frontend/src/context/use-selected-organization.ts (TypeScript)

Code before:
import { useRevalidator } from "react-router";
import { useSelectedOrganizationStore } from "#/stores/selected-organization-store";

export const useSelectedOrganizationId = () => {
  const revalidator = useRevalidator();
  const { organizationId: orgId, setOrganizationId: setOrgIdStore } =
    useSelectedOrganizationStore();

  const setOrgId = (newOrgId: string | null) => {
    setOrgIdStore(newOrgId);
    // Revalidate route to ensure the latest orgId is used.
    // This is useful for redirecting the user away from admin-only org pages.
    revalidator.revalidate();
  };

  return { orgId, setOrgId };
};


Review comment (suggestion):
**suggestion:** This line can be updated accordingly, as we have decided to use the term ‘Organization’ in our code. Therefore, we should consistently use this term. Thank you! 🙏 

```
const { organizationId: currentOrganizationId, setOrganizationId: setOrganizationIdStore } =
    useSelectedOrganizationStore();
```

Code after:
import { useRevalidator } from "react-router";
import { useSelectedOrganizationStore } from "#/stores/selected-organization-store";

export const useSelectedOrganizationId = () => {
  const revalidator = useRevalidator();
  const { organizationId, setOrganizationId: setOrganizationIdStore } =
    useSelectedOrganizationStore();

  const setOrganizationId = (newOrganizationId: string | null) => {
    setOrganizationIdStore(newOrganizationId);
    // Revalidate route to ensure the latest orgId is used.
    // This is useful for redirecting the user away from admin-only org pages.
    revalidator.revalidate();
  };

  return { organizationId, setOrganizationId };
};
Repository: All-Hands-AI/OpenHands
File: openhands/server/types.py (Python)

Code before:
from abc import ABC, abstractmethod
from enum import Enum
from typing import Any, ClassVar, Protocol


class AppMode(Enum):
    OSS = 'oss'
    SAAS = 'saas'


class SessionMiddlewareInterface(Protocol):
    """Protocol for session middleware classes."""

    pass


class ServerConfigInterface(ABC):
    CONFIG_PATH: ClassVar[str | None]
    APP_MODE: ClassVar[AppMode]
    POSTHOG_CLIENT_KEY: ClassVar[str]
    GITHUB_CLIENT_ID: ClassVar[str]
    ATTACH_SESSION_MIDDLEWARE_PATH: ClassVar[str]

    @abstractmethod
    def verify_config(self) -> None:
        """Verify configuration settings."""
        raise NotImplementedError

    @abstractmethod
    def get_config(self) -> dict[str, Any]:
        """Configure attributes for frontend"""
        raise NotImplementedError


class MissingSettingsError(ValueError):
    """Raised when settings are missing or not found."""

    pass


class LLMAuthenticationError(ValueError):
    """Raised when there is an issue with LLM authentication."""

    pass


class SessionExpiredError(ValueError):
    """Raised when the user's authentication session has expired."""

    pass


Review comment (suggestion):
I like to think that maybe the whole `openhands/server/` module will be obsoleted, but maybe `types.py` stays for V1? If not, another spot might be better

Just a tiny thought in passing, nothing big 😅 

Code after:
    ATTACH_SESSION_MIDDLEWARE_PATH: ClassVar[str]

    @abstractmethod
    def verify_config(self) -> None:
        """Verify configuration settings."""
        raise NotImplementedError

    @abstractmethod
    def get_config(self) -> dict[str, Any]:
        """Configure attributes for frontend"""
        raise NotImplementedError


class MissingSettingsError(ValueError):
    """Raised when settings are missing or not found."""

    pass


class LLMAuthenticationError(ValueError):
    """Raised when there is an issue with LLM authentication."""

    pass


class SessionExpiredError(ValueError):
    """Raised when the user's authentication session has expired."""

    pass
Repository: All-Hands-AI/OpenHands
File: frontend/src/components/shared/buttons/new-project-button.tsx (TypeScript)

Code before:

interface NewProjectButtonProps {
  disabled?: boolean;
}

export function NewProjectButton({ disabled = false }: NewProjectButtonProps) {
  const { pathname } = useLocation();
  const { t } = useTranslation();

  const startNewProject = t(I18nKey.CONVERSATION$START_NEW);

  return (
    <StyledTooltip content={startNewProject} placement="right">
      <NavLink
        to="/"
        data-testid="new-project-button"
        aria-label={startNewProject}
        tabIndex={disabled ? -1 : 0}
        onClick={(e) => {
          if (disabled) {
            e.preventDefault();
          }
        }}
        className={`button-base inline-flex items-center justify-center ${
          disabled ? "pointer-events-none opacity-50" : ""
        }`}
      >
        <PlusIcon
          width={24}
          height={24}
          color={pathname === "/" ? "#ffffff" : "#B1B9D3"}
        />
      </NavLink>
    </StyledTooltip>
  );
}


Review comment (suggestion):
```suggestion
        className={cn("button-base inline-flex items-center justify-center", {
          "pointer-events-none opacity-50": disabled,
        })}
```

Use our `cn` util: https://github.com/OpenHands/OpenHands/blob/b5758b160415455976de704e0ead4567b0c74c2d/frontend/src/utils/utils.ts#L11

Code after:
import { cn } from "#/utils/utils";

interface NewProjectButtonProps {
  disabled?: boolean;
}

export function NewProjectButton({ disabled = false }: NewProjectButtonProps) {
  const { t } = useTranslation();

  const startNewProject = t(I18nKey.CONVERSATION$START_NEW);

  return (
    <StyledTooltip
      content={startNewProject}
      placement="right"
      tooltipClassName="bg-transparent"
    >
      <NavLink
        to="/"
        data-testid="new-project-button"
        aria-label={startNewProject}
        tabIndex={disabled ? -1 : 0}
        onClick={(e) => {
          if (disabled) {
            e.preventDefault();
          }
        }}
        className={cn("inline-flex items-center justify-center", {
          "pointer-events-none opacity-50": disabled,
        })}
      >
        <PlusIcon width={24} height={24} />
      </NavLink>
    </StyledTooltip>
  );
}
Repository: All-Hands-AI/OpenHands
File: frontend/__tests__/components/chat/chat-interface.test.tsx (TypeScript)

Code before:
  renderWithProviders(
    <MemoryRouter>
      <ChatInterface />
    </MemoryRouter>,
  );

// Helper function to render with QueryClientProvider and Router (for newer tests)
const renderWithQueryClient = (
  ui: React.ReactElement,
  queryClient: QueryClient,
  route = "/test-conversation-id",
) =>
  render(
    <QueryClientProvider client={queryClient}>
      <MemoryRouter initialEntries={[route]}>
        <Routes>
          <Route path="/:conversationId" element={ui} />
          <Route path="/" element={ui} />
        </Routes>
      </MemoryRouter>
    </QueryClientProvider>,
  );

beforeEach(() => {
  useParamsMock.mockReturnValue({ conversationId: "test-conversation-id" });
  (useConversationId as unknown as ReturnType<typeof vi.fn>).mockReturnValue({
    conversationId: "test-conversation-id",
  });
});

describe("ChatInterface - Chat Suggestions", () => {
  // Create a new QueryClient for each test
  let queryClient: QueryClient;

  beforeEach(() => {
    queryClient = new QueryClient({
      defaultOptions: {
        queries: {
          retry: false,
        },
      },
    });

    (useWsClient as unknown as ReturnType<typeof vi.fn>).mockReturnValue({
      send: vi.fn(),
      isLoadingMessages: false,
      parsedEvents: [],
    });

    useOptimisticUserMessageStore.setState({
      optimisticUserMessage: null,

Review comment (refactor):
```suggestion
  vi.mocked(useConversationId).mockReturnValue({
```

Type helper to avoid casting, see more at https://vitest.dev/api/vi.html#vi-mocked

Code after:
  renderWithProviders(
    <MemoryRouter>
      <ChatInterface />
    </MemoryRouter>,
  );

// Helper function to render with QueryClientProvider and Router (for newer tests)
const renderWithQueryClient = (
  ui: React.ReactElement,
  queryClient: QueryClient,
  route = "/test-conversation-id",
) =>
  render(
    <QueryClientProvider client={queryClient}>
      <MemoryRouter initialEntries={[route]}>
        <Routes>
          <Route path="/:conversationId" element={ui} />
          <Route path="/" element={ui} />
        </Routes>
      </MemoryRouter>
    </QueryClientProvider>,
  );

beforeEach(() => {
  useParamsMock.mockReturnValue({ conversationId: "test-conversation-id" });
  vi.mocked(useConversationId).mockReturnValue({
    conversationId: "test-conversation-id",
  });
});

describe("ChatInterface - Chat Suggestions", () => {
  // Create a new QueryClient for each test
  let queryClient: QueryClient;

  beforeEach(() => {
    queryClient = new QueryClient({
      defaultOptions: {
        queries: {
          retry: false,
        },
      },
    });

    (useWsClient as unknown as ReturnType<typeof vi.fn>).mockReturnValue({
      send: vi.fn(),
      isLoadingMessages: false,
      parsedEvents: [],
    });

    useOptimisticUserMessageStore.setState({
      optimisticUserMessage: null,
Repository: All-Hands-AI/OpenHands
File: frontend/src/components/features/conversation-panel/conversation-card/conversation-card-skeleton.tsx (TypeScript)

Code before:
import React from "react";

export function ConversationCardSkeleton() {
  return (
    <div
      data-testid="conversation-card-skeleton"
      className="relative h-auto w-full p-3.5 border-b border-neutral-600"
    >
      <div className="flex items-center justify-between w-full">
        <div className="flex items-center gap-2 w-full">
          <div className="skeleton-round h-3 w-3" />
          <div className="skeleton h-4 w-2/3 rounded" />
        </div>
        <div className="skeleton-round h-4 w-4 ml-2" />
      </div>
      <div className="mt-2 flex flex-col gap-1">
        <div className="skeleton h-3 w-1/2 rounded" />
        <div className="skeleton h-3 w-1/4 rounded" />
      </div>
    </div>
  );
}


Review comment (refactor):
```suggestion
```

The actual component here is an ellipses, not a circle. I’d prefer to remove this

Code after:
import React from "react";

export function ConversationCardSkeleton() {
  return (
    <div
      data-testid="conversation-card-skeleton"
      className="relative h-auto w-full p-3.5 border-b border-neutral-600"
    >
      <div className="flex items-center justify-between w-full">
        <div className="flex items-center gap-2 w-full">
          <div className="skeleton-round h-1.5 w-1.5" />
          <div className="skeleton h-3 w-2/3 rounded" />
        </div>
      </div>
      <div className="mt-2 flex flex-col gap-1">
        <div className="skeleton h-2 w-1/2 rounded" />
        <div className="flex justify-between">
          <div className="skeleton h-2 w-1/4 rounded" />
          <div className="skeleton h-2 w-8 rounded" />
        </div>
      </div>
    </div>
  );
}
Repository: All-Hands-AI/OpenHands
File: frontend/src/components/features/conversation-panel/conversation-card/conversation-card-skeleton.tsx (TypeScript)

Code before:
import React from "react";

export function ConversationCardSkeleton() {
  return (
    <div
      data-testid="conversation-card-skeleton"
      className="relative h-auto w-full p-3.5 border-b border-neutral-600"
    >
      <div className="flex items-center justify-between w-full">
        <div className="flex items-center gap-2 w-full">
          <div className="skeleton-round h-3 w-3" />
          <div className="skeleton h-4 w-2/3 rounded" />
        </div>
        <div className="skeleton-round h-4 w-4 ml-2" />
      </div>
      <div className="mt-2 flex flex-col gap-1">
        <div className="skeleton h-3 w-1/2 rounded" />
        <div className="skeleton h-3 w-1/4 rounded" />
      </div>
    </div>
  );
}


Review comment (suggestion):
```suggestion
          <div className="skeleton-round h-1.5 w-1.5" />
```

More representative of the actual size of the indicator

Code after:
import React from "react";

export function ConversationCardSkeleton() {
  return (
    <div
      data-testid="conversation-card-skeleton"
      className="relative h-auto w-full p-3.5 border-b border-neutral-600"
    >
      <div className="flex items-center justify-between w-full">
        <div className="flex items-center gap-2 w-full">
          <div className="skeleton-round h-1.5 w-1.5" />
          <div className="skeleton h-3 w-2/3 rounded" />
        </div>
      </div>
      <div className="mt-2 flex flex-col gap-1">
        <div className="skeleton h-2 w-1/2 rounded" />
        <div className="flex justify-between">
          <div className="skeleton h-2 w-1/4 rounded" />
          <div className="skeleton h-2 w-8 rounded" />
        </div>
      </div>
    </div>
  );
}
Repository: All-Hands-AI/OpenHands
File: frontend/src/components/features/conversation-panel/conversation-card/conversation-card-skeleton.tsx (TypeScript)

Code before:
import React from "react";

export function ConversationCardSkeleton() {
  return (
    <div
      data-testid="conversation-card-skeleton"
      className="relative h-auto w-full p-3.5 border-b border-neutral-600"
    >
      <div className="flex items-center justify-between w-full">
        <div className="flex items-center gap-2 w-full">
          <div className="skeleton-round h-3 w-3" />
          <div className="skeleton h-4 w-2/3 rounded" />
        </div>
        <div className="skeleton-round h-4 w-4 ml-2" />
      </div>
      <div className="mt-2 flex flex-col gap-1">
        <div className="skeleton h-3 w-1/2 rounded" />
        <div className="skeleton h-3 w-1/4 rounded" />
      </div>
    </div>
  );
}


Review comment (suggestion):
```suggestion
        <div className="flex justify-between">
          <div className="skeleton h-3 w-1/4 rounded" />
          <div className="skeleton h-3 w-8 rounded" />
        </div>
```

Skeleton for the timestamp

Code after:
import React from "react";

export function ConversationCardSkeleton() {
  return (
    <div
      data-testid="conversation-card-skeleton"
      className="relative h-auto w-full p-3.5 border-b border-neutral-600"
    >
      <div className="flex items-center justify-between w-full">
        <div className="flex items-center gap-2 w-full">
          <div className="skeleton-round h-1.5 w-1.5" />
          <div className="skeleton h-3 w-2/3 rounded" />
        </div>
      </div>
      <div className="mt-2 flex flex-col gap-1">
        <div className="skeleton h-2 w-1/2 rounded" />
        <div className="flex justify-between">
          <div className="skeleton h-2 w-1/4 rounded" />
          <div className="skeleton h-2 w-8 rounded" />
        </div>
      </div>
    </div>
  );
}