From 4a82768e6da2ddf59ed9ff44502b4666b4efa1bf Mon Sep 17 00:00:00 2001 From: Neha Prasad Date: Mon, 5 Jan 2026 19:52:47 +0530 Subject: [PATCH] feat: add empty state to Changes tab with icon and message (#12237) Co-authored-by: amanape <83104063+amanape@users.noreply.github.com> --- .../__tests__/routes/changes-tab.test.tsx | 68 +++++++++++++++++++ .../diff-viewer/empty-changes-message.tsx | 16 +++++ .../components/features/tips/random-tip.tsx | 34 +++++----- frontend/src/i18n/translation.json | 28 ++++---- frontend/src/routes/changes-tab.tsx | 4 ++ 5 files changed, 120 insertions(+), 30 deletions(-) create mode 100644 frontend/__tests__/routes/changes-tab.test.tsx create mode 100644 frontend/src/components/features/diff-viewer/empty-changes-message.tsx diff --git a/frontend/__tests__/routes/changes-tab.test.tsx b/frontend/__tests__/routes/changes-tab.test.tsx new file mode 100644 index 0000000000..178bb28c40 --- /dev/null +++ b/frontend/__tests__/routes/changes-tab.test.tsx @@ -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 }) => ( + + + {children} + + +); + +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(, { 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(, { wrapper }); + + expect( + screen.queryByText("DIFF_VIEWER$NO_CHANGES"), + ).not.toBeInTheDocument(); + }); +}); diff --git a/frontend/src/components/features/diff-viewer/empty-changes-message.tsx b/frontend/src/components/features/diff-viewer/empty-changes-message.tsx new file mode 100644 index 0000000000..6196d2b324 --- /dev/null +++ b/frontend/src/components/features/diff-viewer/empty-changes-message.tsx @@ -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 ( +
+ + + {t(I18nKey.DIFF_VIEWER$NO_CHANGES)} + +
+ ); +} diff --git a/frontend/src/components/features/tips/random-tip.tsx b/frontend/src/components/features/tips/random-tip.tsx index 6843b58160..d3e9a495e4 100644 --- a/frontend/src/components/features/tips/random-tip.tsx +++ b/frontend/src/components/features/tips/random-tip.tsx @@ -13,22 +13,24 @@ export function RandomTip() { }, []); return ( -

+

{t(I18nKey.TIPS$PROTIP)}:

- {t(randomTip.key)} - {randomTip.link && ( - <> - {" "} - - {t(I18nKey.TIPS$LEARN_MORE)} - - - )} -

+

+ {t(randomTip.key)} + {randomTip.link && ( + <> + {" "} + + {t(I18nKey.TIPS$LEARN_MORE)} + + + )} +

+
); } diff --git a/frontend/src/i18n/translation.json b/frontend/src/i18n/translation.json index bfad5665e4..5c1f36af07 100644 --- a/frontend/src/i18n/translation.json +++ b/frontend/src/i18n/translation.json @@ -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...", diff --git a/frontend/src/routes/changes-tab.tsx b/frontend/src/routes/changes-tab.tsx index 7e56d0ab0c..08d1e345bb 100644 --- a/frontend/src/routes/changes-tab.tsx +++ b/frontend/src/routes/changes-tab.tsx @@ -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 && isSuccess && gitChanges.length === 0 && ( + + )}