From 62241e2e00573cb391c681db7619a9e4a0b064d0 Mon Sep 17 00:00:00 2001 From: Mohammed Abdulai <65478290+mo-abdulai@users.noreply.github.com> Date: Mon, 2 Mar 2026 06:45:29 -0500 Subject: [PATCH] Fix: OSS suggested tasks empty state (#12563) Co-authored-by: Mohammed Abdulai Co-authored-by: hieptl --- frontend/__tests__/task-suggestions.test.tsx | 76 +++++++++++++++++++ .../features/home/tasks/task-suggestions.tsx | 41 ++++++++-- frontend/src/i18n/declaration.ts | 3 + frontend/src/i18n/translation.json | 50 ++++++++++++ 4 files changed, 165 insertions(+), 5 deletions(-) create mode 100644 frontend/__tests__/task-suggestions.test.tsx diff --git a/frontend/__tests__/task-suggestions.test.tsx b/frontend/__tests__/task-suggestions.test.tsx new file mode 100644 index 0000000000..bda826e09c --- /dev/null +++ b/frontend/__tests__/task-suggestions.test.tsx @@ -0,0 +1,76 @@ +import { render, screen } from "@testing-library/react"; +import { describe, it, expect, beforeEach, vi } from "vitest"; +import { MemoryRouter } from "react-router"; +import { TaskSuggestions } from "#/components/features/home/tasks/task-suggestions"; + +// Mock translation +vi.mock("react-i18next", () => ({ + useTranslation: () => ({ + t: (key: string) => key, + }), +})); + +// Mock suggested tasks hook +vi.mock("#/hooks/query/use-suggested-tasks", () => ({ + useSuggestedTasks: () => ({ + data: [], + isLoading: false, + }), +})); + +// Mock config hook +vi.mock("#/hooks/query/use-config", () => ({ + useConfig: vi.fn(), +})); + +// Mock user providers hook +vi.mock("#/hooks/use-user-providers", () => ({ + useUserProviders: vi.fn(), +})); + +import { useConfig } from "#/hooks/query/use-config"; +import { useUserProviders } from "#/hooks/use-user-providers"; + +describe("TaskSuggestions empty states", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("OSS mode + no providers → shows Git provider empty state", () => { + (useConfig as any).mockReturnValue({ + data: { app_mode: "oss" }, + }); + + (useUserProviders as any).mockReturnValue({ + providers: [], + }); + + render( + + + + ); + + expect(screen.getByText("TASKS$NO_GIT_PROVIDERS_TITLE")).toBeInTheDocument(); + expect(screen.getByText("TASKS$NO_GIT_PROVIDERS_DESCRIPTION")).toBeInTheDocument(); + expect(screen.getByText("TASKS$NO_GIT_PROVIDERS_CTA")).toBeInTheDocument(); + }); + + it("OSS mode + providers exist but no tasks → shows no tasks message", () => { + (useConfig as any).mockReturnValue({ + data: { app_mode: "oss" }, + }); + + (useUserProviders as any).mockReturnValue({ + providers: [{ id: "github" }], + }); + + render( + + + + ); + + expect(screen.getByText("TASKS$NO_TASKS_AVAILABLE")).toBeInTheDocument(); + }); +}); diff --git a/frontend/src/components/features/home/tasks/task-suggestions.tsx b/frontend/src/components/features/home/tasks/task-suggestions.tsx index 5313991fde..45957e7133 100644 --- a/frontend/src/components/features/home/tasks/task-suggestions.tsx +++ b/frontend/src/components/features/home/tasks/task-suggestions.tsx @@ -1,11 +1,15 @@ import { useState } from "react"; import { useTranslation } from "react-i18next"; +import { Link } from "react-router"; import { TaskGroup } from "./task-group"; import { useSuggestedTasks } from "#/hooks/query/use-suggested-tasks"; import { TaskSuggestionsSkeleton } from "./task-suggestions-skeleton"; import { cn, getDisplayedTaskGroups, getTotalTaskCount } from "#/utils/utils"; import { I18nKey } from "#/i18n/declaration"; import { GitRepository } from "#/types/git"; +import { useConfig } from "#/hooks/query/use-config"; +import { useUserProviders } from "#/hooks/use-user-providers"; +import { Typography } from "#/ui/typography"; interface TaskSuggestionsProps { filterFor?: GitRepository | null; @@ -14,7 +18,13 @@ interface TaskSuggestionsProps { export function TaskSuggestions({ filterFor }: TaskSuggestionsProps) { const { t } = useTranslation(); const [isExpanded, setIsExpanded] = useState(false); + + const { data: config } = useConfig(); const { data: tasks, isLoading } = useSuggestedTasks(); + const { providers } = useUserProviders(); + + const isOSS = config?.app_mode === "oss"; + const hasNoProviders = isOSS && providers.length === 0; const suggestedTasks = filterFor ? tasks?.filter( @@ -63,11 +73,32 @@ export function TaskSuggestions({ filterFor }: TaskSuggestionsProps) { )} - {!hasSuggestedTasks && !isLoading && ( - - {t(I18nKey.TASKS$NO_TASKS_AVAILABLE)} - - )} + {!hasSuggestedTasks && + !isLoading && + (hasNoProviders ? ( +
+ + {t(I18nKey.TASKS$NO_GIT_PROVIDERS_TITLE)} + + + + {t(I18nKey.TASKS$NO_GIT_PROVIDERS_DESCRIPTION)} + + + + + {t(I18nKey.TASKS$NO_GIT_PROVIDERS_CTA)} + + +
+ ) : ( + + {t(I18nKey.TASKS$NO_TASKS_AVAILABLE)} + + ))} {!isLoading && displayedTaskGroups && diff --git a/frontend/src/i18n/declaration.ts b/frontend/src/i18n/declaration.ts index 9129724374..062112460b 100644 --- a/frontend/src/i18n/declaration.ts +++ b/frontend/src/i18n/declaration.ts @@ -722,6 +722,9 @@ export enum I18nKey { HOME$ADD_GITHUB_REPOS = "HOME$ADD_GITHUB_REPOS", REPOSITORY$SELECT_BRANCH = "REPOSITORY$SELECT_BRANCH", REPOSITORY$SELECT_REPO = "REPOSITORY$SELECT_REPO", + TASKS$NO_GIT_PROVIDERS_TITLE = "TASKS$NO_GIT_PROVIDERS_TITLE", + TASKS$NO_GIT_PROVIDERS_DESCRIPTION = "TASKS$NO_GIT_PROVIDERS_DESCRIPTION", + TASKS$NO_GIT_PROVIDERS_CTA = "TASKS$NO_GIT_PROVIDERS_CTA", TASKS$SUGGESTED_TASKS = "TASKS$SUGGESTED_TASKS", TASKS$NO_TASKS_AVAILABLE = "TASKS$NO_TASKS_AVAILABLE", TASKS$TASK_SUGGESTIONS_INFO = "TASKS$TASK_SUGGESTIONS_INFO", diff --git a/frontend/src/i18n/translation.json b/frontend/src/i18n/translation.json index 9f96c50e43..b0c4a0f5ae 100644 --- a/frontend/src/i18n/translation.json +++ b/frontend/src/i18n/translation.json @@ -11553,6 +11553,56 @@ "de": "Ein Repository auswählen", "uk": "Вибрати репозиторій" }, + "TASKS$NO_GIT_PROVIDERS_TITLE": { + "en": "Connect a Git provider", + "ja": "Gitプロバイダーを接続", + "zh-CN": "连接 Git 提供商", + "zh-TW": "連接 Git 提供商", + "ko-KR": "Git 제공자 연결", + "no": "Koble til en Git-leverandør", + "ar": "قم بتوصيل موفر Git", + "de": "Git-Anbieter verbinden", + "fr": "Connecter un fournisseur Git", + "it": "Collega un provider Git", + "pt": "Conectar um provedor Git", + "es": "Conectar un proveedor Git", + "tr": "Git sağlayıcısını bağla", + "uk": "Підключити постачальник Git" + }, + + "TASKS$NO_GIT_PROVIDERS_DESCRIPTION": { + "en": "Connect a Git provider to see suggested tasks from your repositories.", + "ja": "Gitプロバイダーを接続して、リポジトリからの提案タスクを表示します。", + "zh-CN": "连接 Git 提供商以查看来自存储库的建议任务。", + "zh-TW": "連接 Git 提供商以查看來自存儲庫的建議任務。", + "ko-KR": "Git 제공자를 연결하여 저장소의 추천 작업을 확인하세요.", + "no": "Koble til en Git-leverandør for å se foreslåtte oppgaver fra dine repositorier.", + "ar": "قم بتوصيل موفر Git لرؤية المهام المقترحة من مستودعاتك.", + "de": "Verbinde einen Git-Anbieter, um vorgeschlagene Aufgaben aus deinen Repositories zu sehen.", + "fr": "Connectez un fournisseur Git pour voir les tâches suggérées depuis vos dépôts.", + "it": "Collega un provider Git per vedere le attività suggerite dai tuoi repository.", + "pt": "Conecte um provedor Git para ver tarefas sugeridas dos seus repositórios.", + "es": "Conecta un proveedor Git para ver tareas sugeridas de tus repositorios.", + "tr": "Depolarınızdan önerilen görevleri görmek için bir Git sağlayıcısı bağlayın.", + "uk": "Підключіть постачальник Git, щоб бачити запропоновані завдання з ваших репозиторіїв." + }, + + "TASKS$NO_GIT_PROVIDERS_CTA": { + "en": "Go to Integrations", + "ja": "統合へ移動", + "zh-CN": "前往集成", + "zh-TW": "前往整合", + "ko-KR": "통합으로 이동", + "no": "Gå til integrasjoner", + "ar": "الانتقال إلى التكاملات", + "de": "Zu Integrationen", + "fr": "Aller aux intégrations", + "it": "Vai alle integrazioni", + "pt": "Ir para integrações", + "es": "Ir a integraciones", + "tr": "Entegrasyonlara git", + "uk": "Перейти до інтеграцій" + }, "TASKS$SUGGESTED_TASKS": { "en": "Suggested Tasks", "ja": "推奨タスク",