feat: add empty state to Changes tab with icon and message (#12237)

Co-authored-by: amanape <83104063+amanape@users.noreply.github.com>
This commit is contained in:
Neha Prasad
2026-01-05 19:52:47 +05:30
committed by GitHub
parent 6f86e589c8
commit 4a82768e6d
5 changed files with 120 additions and 30 deletions

View File

@@ -0,0 +1,68 @@
import { render, screen } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { describe, expect, it, vi } from "vitest";
import { MemoryRouter } from "react-router";
import GitChanges from "#/routes/changes-tab";
import { useUnifiedGetGitChanges } from "#/hooks/query/use-unified-get-git-changes";
import { useAgentState } from "#/hooks/use-agent-state";
import { AgentState } from "#/types/agent-state";
vi.mock("react-i18next", () => ({
useTranslation: () => ({
t: (key: string) => key,
}),
}));
vi.mock("#/hooks/query/use-unified-get-git-changes");
vi.mock("#/hooks/use-agent-state");
vi.mock("#/hooks/use-conversation-id", () => ({
useConversationId: () => ({ conversationId: "test-id" }),
}));
const wrapper = ({ children }: { children: React.ReactNode }) => (
<MemoryRouter>
<QueryClientProvider client={new QueryClient()}>
{children}
</QueryClientProvider>
</MemoryRouter>
);
describe("Changes Tab", () => {
it("should show EmptyChangesMessage when there are no changes", () => {
vi.mocked(useUnifiedGetGitChanges).mockReturnValue({
data: [],
isLoading: false,
isSuccess: true,
isError: false,
error: null,
refetch: vi.fn(),
});
vi.mocked(useAgentState).mockReturnValue({
curAgentState: AgentState.RUNNING,
});
render(<GitChanges />, { wrapper });
expect(screen.getByText("DIFF_VIEWER$NO_CHANGES")).toBeInTheDocument();
});
it("should not show EmptyChangesMessage when there are changes", () => {
vi.mocked(useUnifiedGetGitChanges).mockReturnValue({
data: [{ path: "src/file.ts", status: "M" }],
isLoading: false,
isSuccess: true,
isError: false,
error: null,
refetch: vi.fn(),
});
vi.mocked(useAgentState).mockReturnValue({
curAgentState: AgentState.RUNNING,
});
render(<GitChanges />, { wrapper });
expect(
screen.queryByText("DIFF_VIEWER$NO_CHANGES"),
).not.toBeInTheDocument();
});
});

View File

@@ -0,0 +1,16 @@
import { useTranslation } from "react-i18next";
import { FaCodeCompare } from "react-icons/fa6";
import { I18nKey } from "#/i18n/declaration";
export function EmptyChangesMessage() {
const { t } = useTranslation();
return (
<div className="flex flex-col items-center justify-center w-full h-full p-10 gap-4">
<FaCodeCompare size={100} className="text-[#A1A1A1]" />
<span className="text-[#8D95A9] text-[19px] font-normal leading-5">
{t(I18nKey.DIFF_VIEWER$NO_CHANGES)}
</span>
</div>
);
}

View File

@@ -13,22 +13,24 @@ export function RandomTip() {
}, []);
return (
<p>
<div>
<h4 className="font-bold">{t(I18nKey.TIPS$PROTIP)}:</h4>
{t(randomTip.key)}
{randomTip.link && (
<>
{" "}
<a
href={randomTip.link}
target="_blank"
rel="noopener noreferrer"
className="underline"
>
{t(I18nKey.TIPS$LEARN_MORE)}
</a>
</>
)}
</p>
<p>
{t(randomTip.key)}
{randomTip.link && (
<>
{" "}
<a
href={randomTip.link}
target="_blank"
rel="noopener noreferrer"
className="underline"
>
{t(I18nKey.TIPS$LEARN_MORE)}
</a>
</>
)}
</p>
</div>
);
}

View File

@@ -10432,20 +10432,20 @@
"uk": "Попросіть OpenHands ініціалізувати git-репозиторій, щоб активувати цей інтерфейс користувача."
},
"DIFF_VIEWER$NO_CHANGES": {
"en": "OpenHands hasn't made any changes yet...",
"ja": "OpenHandsはまだ変更を加えていません...",
"zh-CN": "OpenHands尚未进行任何更改...",
"zh-TW": "OpenHands尚未進行任何更改...",
"ko-KR": "OpenHands는 아직 변경하지 않았습니다...",
"no": "OpenHands har ikke gjort noen endringer ennå...",
"it": "OpenHands non ha ancora apportato modifiche...",
"pt": "O OpenHands ainda não fez nenhuma alteração...",
"es": "OpenHands aún no ha realizado ningún cambio...",
"ar": "لم يقم OpenHands بإجراء أي تغييرات بعد ...",
"fr": "OpenHands n'a pas encore apporté de modifications ...",
"tr": "OpenHands henüz herhangi bir değişiklik yapmadı ...",
"de": "OpenHands hat noch keine Änderungen vorgenommen...",
"uk": "OpenHands ще не вніс жодних змін..."
"en": "OpenHands hasn't made any changes yet",
"ja": "OpenHandsはまだ変更を加えていません",
"zh-CN": "OpenHands尚未进行任何更改",
"zh-TW": "OpenHands尚未進行任何更改",
"ko-KR": "OpenHands는 아직 변경하지 않았습니다",
"no": "OpenHands har ikke gjort noen endringer ennå",
"it": "OpenHands non ha ancora apportato modifiche",
"pt": "O OpenHands ainda não fez nenhuma alteração",
"es": "OpenHands aún no ha realizado ningún cambio",
"ar": "لم يقم OpenHands بإجراء أي تغييرات بعد",
"fr": "OpenHands n'a pas encore apporté de modifications",
"tr": "OpenHands henüz herhangi bir değişiklik yapmadı",
"de": "OpenHands hat noch keine Änderungen vorgenommen",
"uk": "OpenHands ще не вніс жодних змін"
},
"DIFF_VIEWER$WAITING_FOR_RUNTIME": {
"en": "Waiting for runtime to start...",

View File

@@ -1,6 +1,7 @@
import { useTranslation } from "react-i18next";
import React from "react";
import { FileDiffViewer } from "#/components/features/diff-viewer/file-diff-viewer";
import { EmptyChangesMessage } from "#/components/features/diff-viewer/empty-changes-message";
import { retrieveAxiosErrorMessage } from "#/utils/retrieve-axios-error-message";
import { useUnifiedGetGitChanges } from "#/hooks/query/use-unified-get-git-changes";
import { I18nKey } from "#/i18n/declaration";
@@ -77,6 +78,9 @@ function GitChanges() {
))}
</StatusMessage>
)}
{!statusMessage && isSuccess && gitChanges.length === 0 && (
<EmptyChangesMessage />
)}
</div>
<div className="absolute inset-x-0 bottom-0">