Skip to content

feat(x2a): Adding Projects Details Page, fixes in the Modules Details Page#2448

Open
mareklibra wants to merge 2 commits intoredhat-developer:mainfrom
mareklibra:FLPATH-3143.projectDetailPage
Open

feat(x2a): Adding Projects Details Page, fixes in the Modules Details Page#2448
mareklibra wants to merge 2 commits intoredhat-developer:mainfrom
mareklibra:FLPATH-3143.projectDetailPage

Conversation

@mareklibra
Copy link
Member

@mareklibra mareklibra commented Mar 4, 2026

Fixes: FLPATH-3143

Adding Projects Details Page.
Fixed various issues in the Modules Details Page.

TODO:

  • Reposition breadcrumbs - the issue goes back and forth, probably no clean support from Backstage components is available
  • add migratin plan to the project details page

Left for follow-ups:

  • clean-up the landing Project List page (like remove the DetailsPanel)
  • Project actions on the Details Page
  • Rearrange the Modules Page based on designs. This must be aligned with the Project Details Page and better to be done in a follow-up.
Screenshot From 2026-03-05 11-12-31

✔️ Checklist

  • A changeset describing the change and affected packages. (more info)
  • Added or Updated documentation
  • Tests for new functionality and regression tests for bug fixes
  • Screenshots attached (for UI changes)

@rhdh-gh-app
Copy link

rhdh-gh-app bot commented Mar 4, 2026

Missing Changesets

The following package(s) are changed by this PR but do not have a changeset:

  • @red-hat-developer-hub/backstage-plugin-scaffolder-backend-module-x2a

See CONTRIBUTING.md for more information about how to add changesets.

Unexpected Changesets

The following changeset(s) reference packages that have not been changed in this PR:

  • /home/runner/work/rhdh-plugins/rhdh-plugins/workspaces/x2a/.changeset/eight-jokes-invent.md: @red-hat-developer-hub/backstage-plugin-x2a-scaffolder

Note that only changes that affect the published package require changesets, for example changes to tests and storybook stories do not require changesets.

Changed Packages

Package Name Package Path Changeset Bump Current Version
@red-hat-developer-hub/backstage-plugin-scaffolder-backend-module-x2a workspaces/x2a/plugins/scaffolder-backend-module-x2a none v0.1.1
@red-hat-developer-hub/backstage-plugin-x2a-backend workspaces/x2a/plugins/x2a-backend patch v1.0.1
@red-hat-developer-hub/backstage-plugin-x2a-common workspaces/x2a/plugins/x2a-common patch v1.0.1
@red-hat-developer-hub/backstage-plugin-x2a workspaces/x2a/plugins/x2a patch v1.0.1

@rhdh-qodo-merge
Copy link

Review Summary by Qodo

Add Projects Details Page and init phase logs endpoint

✨ Enhancement 🧪 Tests

Grey Divider

Walkthroughs

Description
• Add new /projects/:projectId/log endpoint for init phase logs
• Create ProjectPage component with project details and modules overview
• Refactor PhaseDetails component for reusability across init and module phases
• Expand Project model to include initJob field with sensitive data removed
• Add comprehensive test coverage for init phase log retrieval
• Update translations across multiple languages for new UI components
• Reorganize component structure and improve code reusability
Diagram
flowchart LR
  A["Backend Router"] -->|GET /projects/:projectId/log| B["Init Phase Logs"]
  C["Project Model"] -->|includes initJob| D["ProjectPage Component"]
  D -->|displays| E["ProjectDetailsCard"]
  D -->|displays| F["ProjectModulesCard"]
  D -->|displays| G["InitPhaseCard"]
  G -->|uses| H["PhaseDetails Component"]
  I["ModulePage"] -->|refactored to use| H
Loading

Grey Divider

File Changes

1. workspaces/x2a/plugins/x2a-backend/src/router/jobs.ts ✨ Enhancement +77/-2

Implement init phase log endpoint with permissions

workspaces/x2a/plugins/x2a-backend/src/router/jobs.ts


2. workspaces/x2a/plugins/x2a-backend/src/router/jobs.test.ts 🧪 Tests +256/-0

Add comprehensive tests for init phase logs

workspaces/x2a/plugins/x2a-backend/src/router/jobs.test.ts


3. workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/index.ts ✨ Enhancement +2/-0

Add initJob to project enrichment logic

workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/index.ts


View more (41)
4. workspaces/x2a/plugins/x2a-backend/src/schema/openapi/generated/apis/Api.server.ts ✨ Enhancement +14/-0

Add ProjectsProjectIdLogGet API type definition

workspaces/x2a/plugins/x2a-backend/src/schema/openapi/generated/apis/Api.server.ts


5. workspaces/x2a/plugins/x2a-backend/src/schema/openapi/generated/models/Project.model.ts ✨ Enhancement +2/-0

Add initJob optional field to Project model

workspaces/x2a/plugins/x2a-backend/src/schema/openapi/generated/models/Project.model.ts


6. workspaces/x2a/plugins/x2a-backend/src/schema/openapi/generated/router.ts 📝 Documentation +44/-0

Add OpenAPI spec for init phase logs endpoint

workspaces/x2a/plugins/x2a-backend/src/schema/openapi/generated/router.ts


7. workspaces/x2a/plugins/x2a-backend/src/schema/openapi.yaml 📝 Documentation +29/-0

Define init phase logs endpoint in OpenAPI schema

workspaces/x2a/plugins/x2a-backend/src/schema/openapi.yaml


8. workspaces/x2a/plugins/x2a-common/client/src/schema/openapi/generated/apis/Api.client.ts ✨ Enhancement +39/-0

Add client method for init phase logs retrieval

workspaces/x2a/plugins/x2a-common/client/src/schema/openapi/generated/apis/Api.client.ts


9. workspaces/x2a/plugins/x2a-common/client/src/schema/openapi/generated/models/Project.model.ts ✨ Enhancement +2/-0

Add initJob field to client Project model

workspaces/x2a/plugins/x2a-common/client/src/schema/openapi/generated/models/Project.model.ts


10. workspaces/x2a/plugins/x2a-common/report.api.md 📝 Documentation +13/-0

Update API report with new endpoint and model changes

workspaces/x2a/plugins/x2a-common/report.api.md


11. workspaces/x2a/plugins/x2a/src/components/ProjectPage/ProjectPage.tsx ✨ Enhancement +112/-0

Create main project details page component

workspaces/x2a/plugins/x2a/src/components/ProjectPage/ProjectPage.tsx


12. workspaces/x2a/plugins/x2a/src/components/ProjectPage/ProjectDetailsCard.tsx ✨ Enhancement +115/-0

Display project metadata and migration plan

workspaces/x2a/plugins/x2a/src/components/ProjectPage/ProjectDetailsCard.tsx


13. workspaces/x2a/plugins/x2a/src/components/ProjectPage/ProjectModulesCard.tsx ✨ Enhancement +70/-0

Display modules list with status and phase info

workspaces/x2a/plugins/x2a/src/components/ProjectPage/ProjectModulesCard.tsx


14. workspaces/x2a/plugins/x2a/src/components/ProjectPage/InitPhaseCard.tsx ✨ Enhancement +35/-0

Display init phase details and logs

workspaces/x2a/plugins/x2a/src/components/ProjectPage/InitPhaseCard.tsx


15. workspaces/x2a/plugins/x2a/src/components/ProjectPage/ProjectPageBreadcrumb.tsx ✨ Enhancement +22/-21

Add breadcrumb navigation for project page

workspaces/x2a/plugins/x2a/src/components/ProjectPage/ProjectPageBreadcrumb.tsx


16. workspaces/x2a/plugins/x2a/src/components/ProjectPage/index.ts ✨ Enhancement +17/-0

Export ProjectPage component

workspaces/x2a/plugins/x2a/src/components/ProjectPage/index.ts


17. workspaces/x2a/plugins/x2a/src/components/PhaseDetails.tsx ✨ Enhancement +285/-0

Extract and refactor phase details for reuse

workspaces/x2a/plugins/x2a/src/components/PhaseDetails.tsx


18. workspaces/x2a/plugins/x2a/src/components/ModuleStatusCell.tsx ✨ Enhancement +72/-0

Create reusable module status display component

workspaces/x2a/plugins/x2a/src/components/ModuleStatusCell.tsx


19. workspaces/x2a/plugins/x2a/src/components/CurrentPhaseCell.tsx ✨ Enhancement +9/-16

Simplify phase mapper and improve display format

workspaces/x2a/plugins/x2a/src/components/CurrentPhaseCell.tsx


20. workspaces/x2a/plugins/x2a/src/components/Artifacts.tsx ✨ Enhancement +5/-5

Update imports and use Fragment for rendering

workspaces/x2a/plugins/x2a/src/components/Artifacts.tsx


21. workspaces/x2a/plugins/x2a/src/components/ModulePage/ModulePage.tsx ✨ Enhancement +10/-6

Update to use refactored PhaseDetails component

workspaces/x2a/plugins/x2a/src/components/ModulePage/ModulePage.tsx


22. workspaces/x2a/plugins/x2a/src/components/ModulePage/ModulePageBreadcrumb.tsx ✨ Enhancement +15/-4

Add project link to breadcrumb navigation

workspaces/x2a/plugins/x2a/src/components/ModulePage/ModulePageBreadcrumb.tsx


23. workspaces/x2a/plugins/x2a/src/components/ModulePage/PhasesCard.tsx ✨ Enhancement +5/-250

Refactor to use extracted PhaseDetails component

workspaces/x2a/plugins/x2a/src/components/ModulePage/PhasesCard.tsx


24. workspaces/x2a/plugins/x2a/src/components/ModulePage/ModuleDetailsCard.tsx ✨ Enhancement +1/-1

Update empty value translation key

workspaces/x2a/plugins/x2a/src/components/ModulePage/ModuleDetailsCard.tsx


25. workspaces/x2a/plugins/x2a/src/components/ProjectList/ProjectTable.tsx ✨ Enhancement +31/-8

Add project name links to details page

workspaces/x2a/plugins/x2a/src/components/ProjectList/ProjectTable.tsx


26. workspaces/x2a/plugins/x2a/src/components/ProjectList/DetailPanel.tsx ✨ Enhancement +1/-1

Update artifact imports to use shared component

workspaces/x2a/plugins/x2a/src/components/ProjectList/DetailPanel.tsx


27. workspaces/x2a/plugins/x2a/src/components/ProjectList/EmptyProjectList.tsx ✨ Enhancement +4/-0

Add test data seeding hook integration

workspaces/x2a/plugins/x2a/src/components/ProjectList/EmptyProjectList.tsx


28. workspaces/x2a/plugins/x2a/src/components/ProjectList/ProjectList.test.tsx 🧪 Tests +10/-35

Extract mock utilities and add route ref mocking

workspaces/x2a/plugins/x2a/src/components/ProjectList/ProjectList.test.tsx


29. workspaces/x2a/plugins/x2a/src/components/ProjectList/ProjectTable.test.tsx 🧪 Tests +9/-54

Extract mock utilities and add route ref mocking

workspaces/x2a/plugins/x2a/src/components/ProjectList/ProjectTable.test.tsx


30. workspaces/x2a/plugins/x2a/src/components/ProjectStatusCell.tsx ✨ Enhancement +1/-1

Update import path for translation hook

workspaces/x2a/plugins/x2a/src/components/ProjectStatusCell.tsx


31. workspaces/x2a/plugins/x2a/src/components/Repository.tsx ✨ Enhancement +0/-0

Move to shared components directory

workspaces/x2a/plugins/x2a/src/components/Repository.tsx


32. workspaces/x2a/plugins/x2a/src/components/Router.tsx ✨ Enhancement +3/-1

Add route for new ProjectPage component

workspaces/x2a/plugins/x2a/src/components/Router.tsx


33. workspaces/x2a/plugins/x2a/src/routes.ts ✨ Enhancement +6/-0

Add projectRouteRef for project details page

workspaces/x2a/plugins/x2a/src/routes.ts


34. workspaces/x2a/plugins/x2a/src/components/tools/getLastJob.ts ✨ Enhancement +30/-0

Extract utility function for finding last job

workspaces/x2a/plugins/x2a/src/components/tools/getLastJob.ts


35. workspaces/x2a/plugins/x2a/src/components/tools/index.ts ✨ Enhancement +1/-0

Export getLastJob utility function

workspaces/x2a/plugins/x2a/src/components/tools/index.ts


36. workspaces/x2a/plugins/x2a/src/test-utils/mockRouteRef.ts 🧪 Tests +23/-0

Add mock implementation for route ref testing

workspaces/x2a/plugins/x2a/src/test-utils/mockRouteRef.ts


37. workspaces/x2a/plugins/x2a/src/test-utils/projectListTestUtils.ts 🧪 Tests +85/-0

Extract shared test utilities for project list

workspaces/x2a/plugins/x2a/src/test-utils/projectListTestUtils.ts


38. workspaces/x2a/plugins/x2a/src/translations/ref.ts 📝 Documentation +22/-12

Add translation keys for project page components

workspaces/x2a/plugins/x2a/src/translations/ref.ts


39. workspaces/x2a/plugins/x2a/src/translations/de.ts 📝 Documentation +14/-10

Add German translations for project details page

workspaces/x2a/plugins/x2a/src/translations/de.ts


40. workspaces/x2a/plugins/x2a/src/translations/es.ts 📝 Documentation +46/-42

Add Spanish translations for project details page

workspaces/x2a/plugins/x2a/src/translations/es.ts


41. workspaces/x2a/plugins/x2a/src/translations/fr.ts 📝 Documentation +14/-10

Add French translations for project details page

workspaces/x2a/plugins/x2a/src/translations/fr.ts


42. workspaces/x2a/plugins/x2a/src/translations/it.ts 📝 Documentation +14/-10

Add Italian translations for project details page

workspaces/x2a/plugins/x2a/src/translations/it.ts


43. workspaces/x2a/plugins/x2a/report.api.md 📝 Documentation +17/-14

Update API report with new components and exports

workspaces/x2a/plugins/x2a/report.api.md


44. workspaces/x2a/plugins/x2a/src/components/ModuleTable/ModuleTable.tsx Additional files +4/-17

...

workspaces/x2a/plugins/x2a/src/components/ModuleTable/ModuleTable.tsx


Grey Divider

Qodo Logo

@rhdh-qodo-merge
Copy link

rhdh-qodo-merge bot commented Mar 4, 2026

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Seeds test data 🐞 Bug ⛯ Reliability
Description
EmptyProjectList unconditionally calls useSeedTestData(), which creates projects/modules/jobs and
reloads the page. If merged, visiting the empty-state screen will mutate real environments and can
spam job runs/reloads.
Code

workspaces/x2a/plugins/x2a/src/components/ProjectList/EmptyProjectList.tsx[R74-76]

+  // DO NOT MERGE
+  useSeedTestData();
+
Evidence
The empty-state component always invokes the seeding hook, and the hook creates data via API calls
(projects/modules/runs) and forces a full page reload.

workspaces/x2a/plugins/x2a/src/components/ProjectList/EmptyProjectList.tsx[70-76]
workspaces/x2a/plugins/x2a/src/useSeedTestData.ts[26-56]
workspaces/x2a/plugins/x2a/src/useSeedTestData.ts[60-133]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`EmptyProjectList` currently calls `useSeedTestData()` unconditionally. That hook creates real projects/modules/jobs via API calls and then reloads the page, which will corrupt/mutate environments if merged.

### Issue Context
The hook itself is labeled “Never use in production.” The call site is in a user-facing component that will run in normal navigation.

### Fix Focus Areas
- Remove the seeding call and its import entirely, or guard it behind a strict dev-only condition that cannot be enabled in production builds.

- workspaces/x2a/plugins/x2a/src/components/ProjectList/EmptyProjectList.tsx[74-76]
- workspaces/x2a/plugins/x2a/src/useSeedTestData.ts[26-133]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Init log job mismatch 🐞 Bug ✓ Correctness
Description
GET /projects/:projectId/log selects the latest job by phase='init' only, without guaranteeing it is
the project-level init job (module_id NULL). If module-scoped init jobs exist, this endpoint (and
Project.initJob enrichment) can return the wrong job/logs.
Code

workspaces/x2a/plugins/x2a-backend/src/router/jobs.ts[R59-64]

+    // Get latest init job
+    const jobs = await x2aDatabase.listJobs({
+      projectId,
+      phase: 'init',
+      lastJobOnly: true,
+    });
Evidence
The new endpoint queries init jobs without constraining module_id, while the data model/tests
demonstrate init jobs may have moduleId set. Separately, project init jobs are expected to have no
moduleId, and enrichProject now exposes initJob using the same unfiltered selection.

workspaces/x2a/plugins/x2a-backend/src/router/jobs.ts[59-64]
workspaces/x2a/plugins/x2a-backend/src/router/common.test.ts[90-100]
workspaces/x2a/plugins/x2a-backend/src/router/projects.ts[331-339]
workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/index.ts[68-85]
workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/jobOperations.ts[249-255]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The init-log endpoint and `Project.initJob` enrichment select the “latest init job” by `phase='init'` only. If any init job exists with a `moduleId` set, the endpoint/enrichment can pick a module-scoped init job instead of the project-level init job.

### Issue Context
The codebase indicates project init jobs are expected to have no module (`moduleId: undefined`), but tests demonstrate init jobs can carry a moduleId. Today `listJobs()` cannot express `module_id IS NULL` filtering.

### Fix Focus Areas
- Add support in `listJobs`/`JobOperations.listJobs` for `moduleId: null` meaning `whereNull('module_id')`, and update the init log route + `enrichProject` to request `moduleId: null`.
- Alternatively, implement a dedicated DB operation to fetch the latest *project-level* init job.

- workspaces/x2a/plugins/x2a-backend/src/router/jobs.ts[59-64]
- workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/index.ts[68-85]
- workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/jobOperations.ts[234-280]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. ES i18n encoding broken 🐞 Bug ✓ Correctness
Description
The Spanish translation file contains corrupted characters (e.g., "conversi?n", "Descripci?n"),
which will render broken UI text for Spanish users. This looks like an encoding/regeneration issue
and should be corrected before release.
Code

workspaces/x2a/plugins/x2a/src/translations/es.ts[R27-31]

+    'sidebar.x2a.title': 'Hub de conversi?n',
+    'page.title': 'Hub de conversi?n',
    'page.subtitle':
-      'Inicie y realice el seguimiento de las conversiones asíncronas de archivos Chef a playbooks Ansible listos para producción.',
-    'page.devTitle': 'Hub de conversión',
+      'Inicie y realice el seguimiento de las conversiones as?ncronas de archivos Chef a playbooks Ansible listos para producci?n.',
    'table.columns.name': 'Nombre',
-    'table.columns.abbreviation': 'Abreviatura',
Evidence
The committed strings include replacement characters ('?') where accented characters should be
present, indicating text corruption in the source translations.

workspaces/x2a/plugins/x2a/src/translations/es.ts[24-31]
workspaces/x2a/plugins/x2a/src/translations/es.ts[45-49]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`translations/es.ts` contains corrupted Spanish strings with `?` replacement characters instead of accents (e.g., `conversión`, `Descripción`, `Módulos`). This will display broken UI text.

### Issue Context
Other locales appear fine; the issue is localized to the Spanish file and likely caused by an encoding or copy/paste/regeneration problem.

### Fix Focus Areas
- Replace corrupted strings with correct Spanish (UTF-8) equivalents.
- Ensure the file is saved/committed as UTF-8 and the generation pipeline preserves encoding.

- workspaces/x2a/plugins/x2a/src/translations/es.ts[24-80]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@mareklibra mareklibra force-pushed the FLPATH-3143.projectDetailPage branch 3 times, most recently from 38b5e76 to 8b30238 Compare March 4, 2026 14:50
@mareklibra mareklibra force-pushed the FLPATH-3143.projectDetailPage branch 2 times, most recently from 612dabe to 7d8818a Compare March 5, 2026 10:37
@mareklibra mareklibra marked this pull request as ready for review March 5, 2026 11:42
@rhdh-qodo-merge
Copy link

Review Summary by Qodo

Add Project Details Page and init phase logging support

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add new Project Details Page with project information display
• Implement init phase logging endpoint for project discovery phase
• Refactor shared components for reusability across pages
• Update translations and improve module details page
Diagram
flowchart LR
  A["Project List"] -->|Navigate| B["Project Details Page"]
  B -->|Display| C["Project Info Card"]
  B -->|Display| D["Modules Card"]
  B -->|Display| E["Init Phase Card"]
  E -->|Fetch Logs| F["GET /projects/:projectId/log"]
  F -->|Return| G["Init Phase Logs"]
  C -->|Show| H["Migration Plan"]
Loading

Grey Divider

File Changes

1. workspaces/x2a/plugins/x2a/src/components/ProjectPage/ProjectPage.tsx ✨ Enhancement +113/-0

New Project Details Page component

workspaces/x2a/plugins/x2a/src/components/ProjectPage/ProjectPage.tsx


2. workspaces/x2a/plugins/x2a/src/components/ProjectPage/ProjectDetailsCard.tsx ✨ Enhancement +117/-0

Project information display card

workspaces/x2a/plugins/x2a/src/components/ProjectPage/ProjectDetailsCard.tsx


3. workspaces/x2a/plugins/x2a/src/components/ProjectPage/ProjectModulesCard.tsx ✨ Enhancement +78/-0

Project modules list card component

workspaces/x2a/plugins/x2a/src/components/ProjectPage/ProjectModulesCard.tsx


View more (46)
4. workspaces/x2a/plugins/x2a/src/components/ProjectPage/InitPhaseCard.tsx ✨ Enhancement +35/-0

Init phase discovery display card

workspaces/x2a/plugins/x2a/src/components/ProjectPage/InitPhaseCard.tsx


5. workspaces/x2a/plugins/x2a/src/components/ProjectPage/ProjectPageBreadcrumb.tsx ✨ Enhancement +22/-21

Breadcrumb navigation for project page

workspaces/x2a/plugins/x2a/src/components/ProjectPage/ProjectPageBreadcrumb.tsx


6. workspaces/x2a/plugins/x2a-backend/src/router/jobs.ts ✨ Enhancement +77/-2

Add init phase logging endpoint implementation

workspaces/x2a/plugins/x2a-backend/src/router/jobs.ts


7. workspaces/x2a/plugins/x2a-backend/src/router/jobs.test.ts 🧪 Tests +257/-1

Comprehensive tests for init phase logs endpoint

workspaces/x2a/plugins/x2a-backend/src/router/jobs.test.ts


8. workspaces/x2a/plugins/x2a/src/components/PhaseDetails.tsx ✨ Enhancement +287/-0

Refactored phase details component for reuse

workspaces/x2a/plugins/x2a/src/components/PhaseDetails.tsx


9. workspaces/x2a/plugins/x2a/src/components/ModulePage/PhasesCard.tsx ✨ Enhancement +5/-250

Simplified phases card using refactored component

workspaces/x2a/plugins/x2a/src/components/ModulePage/PhasesCard.tsx


10. workspaces/x2a/plugins/x2a/src/components/ModuleStatusCell.tsx ✨ Enhancement +70/-0

New module status cell component

workspaces/x2a/plugins/x2a/src/components/ModuleStatusCell.tsx


11. workspaces/x2a/plugins/x2a/src/components/CurrentPhaseCell.tsx ✨ Enhancement +9/-16

Refactored phase display with improved formatting

workspaces/x2a/plugins/x2a/src/components/CurrentPhaseCell.tsx


12. workspaces/x2a/plugins/x2a/src/components/ProjectList/ProjectTable.tsx ✨ Enhancement +32/-9

Add navigation links to project details page

workspaces/x2a/plugins/x2a/src/components/ProjectList/ProjectTable.tsx


13. workspaces/x2a/plugins/x2a/src/components/ModulePage/ModulePageBreadcrumb.tsx ✨ Enhancement +15/-4

Update breadcrumb with project navigation

workspaces/x2a/plugins/x2a/src/components/ModulePage/ModulePageBreadcrumb.tsx


14. workspaces/x2a/plugins/x2a/src/routes.ts ✨ Enhancement +6/-0

Add project route reference

workspaces/x2a/plugins/x2a/src/routes.ts


15. workspaces/x2a/plugins/x2a/src/components/Router.tsx ✨ Enhancement +3/-1

Register project page route

workspaces/x2a/plugins/x2a/src/components/Router.tsx


16. workspaces/x2a/plugins/x2a/src/components/tools/getLastJob.ts ✨ Enhancement +31/-0

New utility to extract last job from module

workspaces/x2a/plugins/x2a/src/components/tools/getLastJob.ts


17. workspaces/x2a/plugins/x2a/src/test-utils/projectListTestUtils.ts 🧪 Tests +85/-0

Shared test utilities for project list tests

workspaces/x2a/plugins/x2a/src/test-utils/projectListTestUtils.ts


18. workspaces/x2a/plugins/x2a/src/test-utils/mockRouteRef.ts 🧪 Tests +23/-0

Mock route reference for testing

workspaces/x2a/plugins/x2a/src/test-utils/mockRouteRef.ts


19. workspaces/x2a/plugins/x2a/src/translations/ref.ts 📝 Documentation +24/-12

Add project page translation keys

workspaces/x2a/plugins/x2a/src/translations/ref.ts


20. workspaces/x2a/plugins/x2a/src/translations/es.ts 📝 Documentation +20/-14

Spanish translations for project page

workspaces/x2a/plugins/x2a/src/translations/es.ts


21. workspaces/x2a/plugins/x2a/src/translations/it.ts 📝 Documentation +16/-10

Italian translations for project page

workspaces/x2a/plugins/x2a/src/translations/it.ts


22. workspaces/x2a/plugins/x2a/src/translations/fr.ts 📝 Documentation +17/-10

French translations for project page

workspaces/x2a/plugins/x2a/src/translations/fr.ts


23. workspaces/x2a/plugins/x2a/src/translations/de.ts 📝 Documentation +16/-10

German translations for project page

workspaces/x2a/plugins/x2a/src/translations/de.ts


24. workspaces/x2a/plugins/x2a-backend/src/schema/openapi.yaml 📝 Documentation +29/-0

OpenAPI spec for init phase logs endpoint

workspaces/x2a/plugins/x2a-backend/src/schema/openapi.yaml


25. workspaces/x2a/plugins/x2a-backend/src/schema/openapi/generated/router.ts ✨ Enhancement +44/-0

Generated OpenAPI router types

workspaces/x2a/plugins/x2a-backend/src/schema/openapi/generated/router.ts


26. workspaces/x2a/plugins/x2a-common/client/src/schema/openapi/generated/apis/Api.client.ts ✨ Enhancement +39/-0

Generated API client for init logs endpoint

workspaces/x2a/plugins/x2a-common/client/src/schema/openapi/generated/apis/Api.client.ts


27. workspaces/x2a/plugins/x2a-backend/src/schema/openapi/generated/apis/Api.server.ts ✨ Enhancement +14/-0

Generated API server types

workspaces/x2a/plugins/x2a-backend/src/schema/openapi/generated/apis/Api.server.ts


28. workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/index.ts ✨ Enhancement +2/-0

Add init job to project enrichment

workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/index.ts


29. workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/jobOperations.ts 🐞 Bug fix +3/-0

Filter init jobs by null module_id

workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/jobOperations.ts


30. workspaces/x2a/plugins/x2a-backend/src/services/KubeService.ts 🐞 Bug fix +4/-3

Handle missing pod logs gracefully

workspaces/x2a/plugins/x2a-backend/src/services/KubeService.ts


31. workspaces/x2a/plugins/x2a/.changeset/eight-jokes-invent.md 📝 Documentation +0/-0

Changeset for project details page feature

workspaces/x2a/plugins/x2a/.changeset/eight-jokes-invent.md


32. workspaces/x2a/.changeset/eight-jokes-invent.md Additional files +8/-0

...

workspaces/x2a/.changeset/eight-jokes-invent.md


33. workspaces/x2a/plugins/scaffolder-backend-module-x2a/src/actions/createProject.ts Additional files +0/-1

...

workspaces/x2a/plugins/scaffolder-backend-module-x2a/src/actions/createProject.ts


34. workspaces/x2a/plugins/x2a-backend/src/schema/openapi/generated/models/Project.model.ts Additional files +2/-0

...

workspaces/x2a/plugins/x2a-backend/src/schema/openapi/generated/models/Project.model.ts


35. workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/jobs.test.ts Additional files +0/-2

...

workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/jobs.test.ts


36. workspaces/x2a/plugins/x2a-common/client/src/schema/openapi/generated/models/Project.model.ts Additional files +2/-0

...

workspaces/x2a/plugins/x2a-common/client/src/schema/openapi/generated/models/Project.model.ts


37. workspaces/x2a/plugins/x2a-common/report.api.md Additional files +13/-0

...

workspaces/x2a/plugins/x2a-common/report.api.md


38. workspaces/x2a/plugins/x2a/report.api.md Additional files +20/-15

...

workspaces/x2a/plugins/x2a/report.api.md


39. workspaces/x2a/plugins/x2a/src/components/Artifacts.tsx Additional files +5/-5

...

workspaces/x2a/plugins/x2a/src/components/Artifacts.tsx


40. workspaces/x2a/plugins/x2a/src/components/ModulePage/ModuleDetailsCard.tsx Additional files +1/-1

...

workspaces/x2a/plugins/x2a/src/components/ModulePage/ModuleDetailsCard.tsx


41. workspaces/x2a/plugins/x2a/src/components/ModulePage/ModulePage.tsx Additional files +14/-8

...

workspaces/x2a/plugins/x2a/src/components/ModulePage/ModulePage.tsx


42. workspaces/x2a/plugins/x2a/src/components/ModuleTable/ModuleTable.tsx Additional files +4/-17

...

workspaces/x2a/plugins/x2a/src/components/ModuleTable/ModuleTable.tsx


43. workspaces/x2a/plugins/x2a/src/components/ProjectList/DetailPanel.tsx Additional files +1/-1

...

workspaces/x2a/plugins/x2a/src/components/ProjectList/DetailPanel.tsx


44. workspaces/x2a/plugins/x2a/src/components/ProjectList/ProjectList.test.tsx Additional files +10/-35

...

workspaces/x2a/plugins/x2a/src/components/ProjectList/ProjectList.test.tsx


45. workspaces/x2a/plugins/x2a/src/components/ProjectList/ProjectTable.test.tsx Additional files +10/-55

...

workspaces/x2a/plugins/x2a/src/components/ProjectList/ProjectTable.test.tsx


46. workspaces/x2a/plugins/x2a/src/components/ProjectPage/index.ts Additional files +17/-0

...

workspaces/x2a/plugins/x2a/src/components/ProjectPage/index.ts


47. workspaces/x2a/plugins/x2a/src/components/ProjectStatusCell.tsx Additional files +1/-1

...

workspaces/x2a/plugins/x2a/src/components/ProjectStatusCell.tsx


48. workspaces/x2a/plugins/x2a/src/components/Repository.tsx Additional files +0/-0

...

workspaces/x2a/plugins/x2a/src/components/Repository.tsx


49. workspaces/x2a/plugins/x2a/src/components/tools/index.ts Additional files +1/-0

...

workspaces/x2a/plugins/x2a/src/components/tools/index.ts


Grey Divider

Qodo Logo

@rhdh-qodo-merge
Copy link

rhdh-qodo-merge bot commented Mar 5, 2026

Code Review by Qodo

🐞 Bugs (5) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. K8s log errors swallowed 🐞 Bug ⛯ Reliability ⭐ New
Description
KubeService.getJobLogs now catches all errors and returns '', hiding real failures
(auth/network/404/etc.) and making clients see “no logs” rather than an actionable error; it can
also lead to empty logs being persisted during reconciliation flows.
Code

workspaces/x2a/plugins/x2a-backend/src/services/KubeService.ts[R430-432]

+      // Might happen, i.e. the pod is in ContainerCreating state
+      this.#logger.warn(`Failed to get job logs: ${error.message}`);
+      return '';
Evidence
The new implementation returns an empty string for any exception, not just known-transient states.
The reconciliation path explicitly expects getJobLogs to throw (it has a try/catch) and will
update the DB with whatever string it receives; with the new behavior, the catch will never run and
an empty log can be stored, permanently losing observability for that job run.

workspaces/x2a/plugins/x2a-backend/src/services/KubeService.ts[401-433]
workspaces/x2a/plugins/x2a-backend/src/router/common.ts[293-330]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`KubeService.getJobLogs` currently swallows *all* errors and returns an empty string. This hides real failures from API clients and also breaks the intent of `reconcileJobStatus` which expects `getJobLogs` to throw on failure.

### Issue Context
Returning `''` for every exception makes "no logs" indistinguishable from "couldn't fetch logs" and can cause empty logs to be written into the DB.

### Fix Focus Areas
- workspaces/x2a/plugins/x2a-backend/src/services/KubeService.ts[401-433]
- workspaces/x2a/plugins/x2a-backend/src/router/common.ts[312-326]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Seeds test data 🐞 Bug ⛯ Reliability
Description
EmptyProjectList unconditionally calls useSeedTestData(), which creates projects/modules/jobs and
reloads the page. If merged, visiting the empty-state screen will mutate real environments and can
spam job runs/reloads.
Code

workspaces/x2a/plugins/x2a/src/components/ProjectList/EmptyProjectList.tsx[R74-76]

+  // DO NOT MERGE
+  useSeedTestData();
+
Evidence
The empty-state component always invokes the seeding hook, and the hook creates data via API calls
(projects/modules/runs) and forces a full page reload.

workspaces/x2a/plugins/x2a/src/components/ProjectList/EmptyProjectList.tsx[70-76]
workspaces/x2a/plugins/x2a/src/useSeedTestData.ts[26-56]
workspaces/x2a/plugins/x2a/src/useSeedTestData.ts[60-133]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`EmptyProjectList` currently calls `useSeedTestData()` unconditionally. That hook creates real projects/modules/jobs via API calls and then reloads the page, which will corrupt/mutate environments if merged.
### Issue Context
The hook itself is labeled “Never use in production.” The call site is in a user-facing component that will run in normal navigation.
### Fix Focus Areas
- Remove the seeding call and its import entirely, or guard it behind a strict dev-only condition that cannot be enabled in production builds.
- workspaces/x2a/plugins/x2a/src/components/ProjectList/EmptyProjectList.tsx[74-76]
- workspaces/x2a/plugins/x2a/src/useSeedTestData.ts[26-133]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Init log route skips reconcile 🐞 Bug ✓ Correctness ⭐ New
Description
The new GET /projects/:projectId/log endpoint decides between DB vs K8s logs based on the DB job
status without reconciling job status against K8s first, so stale DB status can lead to querying K8s
for logs even when the job has actually completed (and pods may already be gone), returning empty
logs.
Code

workspaces/x2a/plugins/x2a-backend/src/router/jobs.ts[R72-109]

+    const latestJob = jobs[0]; // Already sorted by started_at DESC in listJobs
+
+    // If job is finished, return logs from database
+    if (latestJob.status === 'success' || latestJob.status === 'error') {
+      logger.info(
+        `Job ${latestJob.id} is finished (status: ${latestJob.status}), returning logs from database`,
+      );
+      res.setHeader('Content-Type', 'text/plain');
+      const log = await x2aDatabase.getJobLogs({ jobId: latestJob.id });
+      if (!log) {
+        logger.error(`Log not found for a finished job ${latestJob.id}`);
+      }
+      res.send(log || '');
+      return;
+    }
+
+    // Check if job has k8sJobName
+    if (!latestJob.k8sJobName) {
+      logger.warn(
+        `Job ${latestJob.id} has no k8sJobName, returning empty logs`,
+      );
+      res.setHeader('Content-Type', 'text/plain');
+      res.send('');
+      return;
+    }
+
+    // Get logs from Kubernetes
+    const logs = await kubeService.getJobLogs(latestJob.k8sJobName, streaming);
+
+    // Set content type
+    res.setHeader('Content-Type', 'text/plain');
+
+    // Handle streaming vs non-streaming
+    if (streaming && typeof logs !== 'string') {
+      logs.pipe(res);
+    } else {
+      res.send(logs as string);
+    }
Evidence
Other parts of the backend reconcile active init jobs to avoid acting on stale DB state.
reconcileJobStatus is documented as the only mechanism to sync stale DB job records with actual
K8s state, but the new init-log endpoint does not use it and directly uses latestJob.status to
choose DB vs K8s. Combined with getJobLogs returning '' on failures, this can surface as “empty
logs” even when the job is actually finished/failed.

workspaces/x2a/plugins/x2a-backend/src/router/jobs.ts[59-109]
workspaces/x2a/plugins/x2a-backend/src/router/common.ts[293-310]
workspaces/x2a/plugins/x2a-backend/src/router/projects.ts[307-320]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`GET /projects/:projectId/log` uses the DB job status directly to decide whether to return DB logs or fetch K8s logs. If the DB status is stale, the endpoint can fetch from K8s unnecessarily and return empty logs.

### Issue Context
There is an existing `reconcileJobStatus` helper intended to sync stale DB job records with K8s state.

### Fix Focus Areas
- workspaces/x2a/plugins/x2a-backend/src/router/jobs.ts[59-110]
- workspaces/x2a/plugins/x2a-backend/src/router/common.ts[293-334]
- workspaces/x2a/plugins/x2a-backend/src/services/KubeService.ts[401-433]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Init log job mismatch 🐞 Bug ✓ Correctness
Description
GET /projects/:projectId/log selects the latest job by phase='init' only, without guaranteeing it is
the project-level init job (module_id NULL). If module-scoped init jobs exist, this endpoint (and
Project.initJob enrichment) can return the wrong job/logs.
Code

workspaces/x2a/plugins/x2a-backend/src/router/jobs.ts[R59-64]

+    // Get latest init job
+    const jobs = await x2aDatabase.listJobs({
+      projectId,
+      phase: 'init',
+      lastJobOnly: true,
+    });
Evidence
The new endpoint queries init jobs without constraining module_id, while the data model/tests
demonstrate init jobs may have moduleId set. Separately, project init jobs are expected to have no
moduleId, and enrichProject now exposes initJob using the same unfiltered selection.

workspaces/x2a/plugins/x2a-backend/src/router/jobs.ts[59-64]
workspaces/x2a/plugins/x2a-backend/src/router/common.test.ts[90-100]
workspaces/x2a/plugins/x2a-backend/src/router/projects.ts[331-339]
workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/index.ts[68-85]
workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/jobOperations.ts[249-255]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The init-log endpoint and `Project.initJob` enrichment select the “latest init job” by `phase='init'` only. If any init job exists with a `moduleId` set, the endpoint/enrichment can pick a module-scoped init job instead of the project-level init job.
### Issue Context
The codebase indicates project init jobs are expected to have no module (`moduleId: undefined`), but tests demonstrate init jobs can carry a moduleId. Today `listJobs()` cannot express `module_id IS NULL` filtering.
### Fix Focus Areas
- Add support in `listJobs`/`JobOperations.listJobs` for `moduleId: null` meaning `whereNull('module_id')`, and update the init log route + `enrichProject` to request `moduleId: null`.
- Alternatively, implement a dedicated DB operation to fetch the latest *project-level* init job.
- workspaces/x2a/plugins/x2a-backend/src/router/jobs.ts[59-64]
- workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/index.ts[68-85]
- workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/jobOperations.ts[234-280]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. ES i18n encoding broken 🐞 Bug ✓ Correctness
Description
The Spanish translation file contains corrupted characters (e.g., "conversi?n", "Descripci?n"),
which will render broken UI text for Spanish users. This looks like an encoding/regeneration issue
and should be corrected before release.
Code

workspaces/x2a/plugins/x2a/src/translations/es.ts[R27-31]

+    'sidebar.x2a.title': 'Hub de conversi?n',
+    'page.title': 'Hub de conversi?n',
   'page.subtitle':
-      'Inicie y realice el seguimiento de las conversiones asíncronas de archivos Chef a playbooks Ansible listos para producción.',
-    'page.devTitle': 'Hub de conversión',
+      'Inicie y realice el seguimiento de las conversiones as?ncronas de archivos Chef a playbooks Ansible listos para producci?n.',
   'table.columns.name': 'Nombre',
-    'table.columns.abbreviation': 'Abreviatura',
Evidence
The committed strings include replacement characters ('?') where accented characters should be
present, indicating text corruption in the source translations.

workspaces/x2a/plugins/x2a/src/translations/es.ts[24-31]
workspaces/x2a/plugins/x2a/src/translations/es.ts[45-49]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`translations/es.ts` contains corrupted Spanish strings with `?` replacement characters instead of accents (e.g., `conversión`, `Descripción`, `Módulos`). This will display broken UI text.
### Issue Context
Other locales appear fine; the issue is localized to the Spanish file and likely caused by an encoding or copy/paste/regeneration problem.
### Fix Focus Areas
- Replace corrupted strings with correct Spanish (UTF-8) equivalents.
- Ensure the file is saved/committed as UTF-8 and the generation pipeline preserves encoding.
- workspaces/x2a/plugins/x2a/src/translations/es.ts[24-80]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Copy link
Contributor

@elai-shalev elai-shalev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments on the code, I still need to run the UI and inspect more deeply

throw error;
// Might happen, i.e. the pod is in ContainerCreating state
this.#logger.warn(`Failed to get job logs: ${error.message}`);
return '';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This handles ContainerCreating gracefully but also hides auth failures, network errors, namespace misconfigurations, etc. Callers (the route handler) cannot distinguish "pod not ready" from "something is broken." Maybe we could catch only the specific error (e.g., check error message/code for pod-not-ready conditions) and re-throw unexpected errors. Or return a richer result type like { log: string; error?: string }.
returning '' may be insufficient, we should throw or return something more substantial

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines +74 to +109
// If job is finished, return logs from database
if (latestJob.status === 'success' || latestJob.status === 'error') {
logger.info(
`Job ${latestJob.id} is finished (status: ${latestJob.status}), returning logs from database`,
);
res.setHeader('Content-Type', 'text/plain');
const log = await x2aDatabase.getJobLogs({ jobId: latestJob.id });
if (!log) {
logger.error(`Log not found for a finished job ${latestJob.id}`);
}
res.send(log || '');
return;
}

// Check if job has k8sJobName
if (!latestJob.k8sJobName) {
logger.warn(
`Job ${latestJob.id} has no k8sJobName, returning empty logs`,
);
res.setHeader('Content-Type', 'text/plain');
res.send('');
return;
}

// Get logs from Kubernetes
const logs = await kubeService.getJobLogs(latestJob.k8sJobName, streaming);

// Set content type
res.setHeader('Content-Type', 'text/plain');

// Handle streaming vs non-streaming
if (streaming && typeof logs !== 'string') {
logs.pipe(res);
} else {
res.send(logs as string);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic in L74-110 is similar to L170-205.
We should extract a shared handleLogRetrieval(job, kubeService, res, streaming) helper function, it's a lot of duplication

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


// Handle streaming vs non-streaming
if (streaming && typeof logs !== 'string') {
logs.pipe(res);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the k8s stream errors mid-transfer, the client receives a truncated response with no error indication and no cleanup. We should add an error listener on the stream (e.g., logs.on('error', ...)) and handle it by ending the response with an error indicator or at least log it server-side.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -0,0 +1,113 @@
/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unlike ModulePage which has a refresh counter to re-fetch after actions, ProjectPage has no way to auto-update. If a user lands here while an init job is running, the status/logs won't update without a manual browser refresh.
Suggestion: Add a refresh state (like ModulePage has) and consider a polling interval while the init job is in pending/running state.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So far we do not ave polling in place, this will be handled by FLPATH-3337 or similar.

The ModulePage refresh is for update after an active action (Run phase).
There is no such action on the project's page so far.

const { projectId } = useRouteRefParams(projectRouteRef);
const clientService = useClientService();
// TODO: call actions - delete, sync
const [error, _] = useState<string | undefined>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the setter is never called, so the error banner at line 88 can never appear

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, it's a placeholder for a next PR. The first one in a queue is for Delete project action which will use that.


import { Module } from '@red-hat-developer-hub/backstage-plugin-x2a-common';

export const getLastJob = (rowData: Module) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should make it clearer that getLastJob isn't about chronology, but "most advanced". is analyze is rerun, we would still expect publish to return?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think we should return the last reached phase, no matter the user stepped back and tries to execute an earlier phase again.

Otherwise we should probably need to hard delete the "more ones on an early-phase retrigger. I would not do it until we receive such requirement based on real production testing, such UX is risky.

Comment on lines +430 to +432
// Might happen, i.e. the pod is in ContainerCreating state
this.#logger.warn(`Failed to get job logs: ${error.message}`);
return '';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. K8s log errors swallowed 🐞 Bug ⛯ Reliability

KubeService.getJobLogs now catches all errors and returns '', hiding real failures
(auth/network/404/etc.) and making clients see “no logs” rather than an actionable error; it can
also lead to empty logs being persisted during reconciliation flows.
Agent Prompt
### Issue description
`KubeService.getJobLogs` currently swallows *all* errors and returns an empty string. This hides real failures from API clients and also breaks the intent of `reconcileJobStatus` which expects `getJobLogs` to throw on failure.

### Issue Context
Returning `''` for every exception makes "no logs" indistinguishable from "couldn't fetch logs" and can cause empty logs to be written into the DB.

### Fix Focus Areas
- workspaces/x2a/plugins/x2a-backend/src/services/KubeService.ts[401-433]
- workspaces/x2a/plugins/x2a-backend/src/router/common.ts[312-326]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

</Grid>

<Grid {...gridItemProps}>
<ItemField
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this be a link? It's a bit weird to have an external url that you cannot click!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure

@elai-shalev
Copy link
Contributor

Could we make these signs more interacive?
image

… Page

Signed-off-by: Marek Libra <marek.libra@gmail.com>
@mareklibra mareklibra force-pushed the FLPATH-3143.projectDetailPage branch 2 times, most recently from bf040c5 to 751177b Compare March 5, 2026 14:27
@mareklibra mareklibra marked this pull request as draft March 5, 2026 14:28
@mareklibra mareklibra force-pushed the FLPATH-3143.projectDetailPage branch from 751177b to 8ebe6e2 Compare March 5, 2026 14:36
@mareklibra mareklibra marked this pull request as ready for review March 5, 2026 14:36
@rhdh-qodo-merge
Copy link

Review Summary by Qodo

Add Project Details Page with init phase logs and improve module details page

✨ Enhancement 🧪 Tests 📝 Documentation

Grey Divider

Walkthroughs

Description
• **Added Project Details Page** with init phase information, migration plan display, and phase
  details card
• **Implemented init phase log endpoint** (GET /projects/:projectId/log) with streaming support
  for retrieving project initialization logs
• **Extracted reusable PhaseDetails component** for displaying phase information and logs,
  supporting both module and init phases
• **Enhanced project enrichment** with initJob field containing the last initialization job
  details
• **Added utility functions** for SCM provider detection (getScmProvider), repository branch URL
  building (buildRepoBranchUrl), and error stringification (stringifyError)
• **Made project names clickable** in the project list table, linking to the new project details
  page
• **Updated translations** across multiple languages (Spanish, Italian, French, German) with new
  keys for project details page and init phase card
• **Improved Kubernetes pod log retrieval** with pending state validation to avoid fetching logs
  from uninitialized pods
• **Fixed various issues** in Module Details Page including translation key updates and import path
  corrections
• **Comprehensive test coverage** added for new endpoints, utilities, and components
Diagram
flowchart LR
  A["Project List"] -->|"click project name"| B["Project Details Page"]
  B --> C["InitPhaseCard"]
  B --> D["ProjectModulesCard"]
  C --> E["PhaseDetails Component"]
  D --> E
  E -->|"fetch logs"| F["GET /projects/:projectId/log"]
  F --> G["Kubernetes Logs"]
  F --> H["Database Logs"]
  I["SCM Provider Utils"] --> J["Build Repo URLs"]
  K["Error Stringify Utils"] --> L["Improved Error Handling"]
Loading

Grey Divider

File Changes

1. workspaces/x2a/plugins/x2a-backend/src/router/jobs.test.ts 🧪 Tests +300/-1

Add init phase log endpoint tests with streaming support

• Added comprehensive test suite for GET /projects/:projectId/log endpoint covering init phase
 logs
• Tests cover success/error status logs from database, running job logs from Kubernetes, streaming
 functionality, error handling, and permission checks
• Added Readable import from node:stream for stream testing
• Updated existing module log tests with LONG_TEST_TIMEOUT parameter

workspaces/x2a/plugins/x2a-backend/src/router/jobs.test.ts


2. workspaces/x2a/plugins/x2a-backend/src/router/jobs.ts ✨ Enhancement +111/-39

Implement init phase log endpoint with streaming support

• Implemented new GET /projects/:projectId/log endpoint for retrieving init phase logs
• Extracted common log retrieval logic into reusable sendJobLogs function supporting both database
 and Kubernetes logs
• Added streaming support with error handling for stream interruptions
• Refactored module log endpoint to use the new sendJobLogs function

workspaces/x2a/plugins/x2a-backend/src/router/jobs.ts


3. workspaces/x2a/plugins/x2a/src/translations/es.ts 📝 Documentation +20/-14

Update Spanish translations for project details page

• Removed deprecated translation keys (page.devTitle, table.detailPanel, wizard keys)
• Added new translation keys for project details page (projectDetailsCard.*,
 projectModulesCard.*, initPhaseCard.*)
• Added empty key and projectPage.title for new project details page
• Added modulePage.phases.noLogsAvailable and modulePage.phases.resyncMigrationPlanInstructions
 keys
• Fixed encoding issues in Spanish text (e.g., Última, Éxito)

workspaces/x2a/plugins/x2a/src/translations/es.ts


View more (55)
4. workspaces/x2a/plugins/x2a/src/components/tools/buildRepoBranchUrl.test.ts 🧪 Tests +115/-0

Add tests for repository branch URL builder utility

• New test file for buildRepoBranchUrl utility function
• Tests cover GitHub and GitLab URL handling with branch encoding
• Tests include edge cases like trailing slashes, .git suffix removal, and nested paths
• Tests for invalid URLs and error handling

workspaces/x2a/plugins/x2a/src/components/tools/buildRepoBranchUrl.test.ts


5. workspaces/x2a/plugins/x2a/src/translations/it.ts 📝 Documentation +16/-10

Update Italian translations for project details page

• Removed deprecated translation keys (page.devTitle, table.detailPanel, wizard keys)
• Added new translation keys for project details page and init phase card
• Added empty key and projectPage.title
• Added modulePage.phases.noLogsAvailable and modulePage.phases.resyncMigrationPlanInstructions
 keys

workspaces/x2a/plugins/x2a/src/translations/it.ts


6. workspaces/x2a/plugins/x2a/src/translations/fr.ts 📝 Documentation +17/-10

Update French translations for project details page

• Removed deprecated translation keys (page.devTitle, table.detailPanel, wizard keys)
• Added new translation keys for project details page, modules card, and init phase card
• Added empty key and projectPage.title
• Added modulePage.phases.noLogsAvailable and modulePage.phases.resyncMigrationPlanInstructions
 keys

workspaces/x2a/plugins/x2a/src/translations/fr.ts


7. workspaces/x2a/plugins/x2a/src/translations/de.ts 📝 Documentation +16/-10

Update German translations for project details page

• Removed deprecated translation keys (page.devTitle, table.detailPanel, wizard keys)
• Added new translation keys for project details page, modules card, and init phase card
• Added empty key and projectPage.title
• Added modulePage.phases.noLogsAvailable and modulePage.phases.resyncMigrationPlanInstructions
 keys

workspaces/x2a/plugins/x2a/src/translations/de.ts


8. workspaces/x2a/plugins/x2a/src/translations/ref.ts 📝 Documentation +24/-12

Add translation keys for project details page

• Added new translation message groups for project page (projectPage, projectDetailsCard,
 projectModulesCard, initPhaseCard)
• Removed deprecated wizard translation keys
• Added empty key for empty value placeholders
• Added modulePage.phases.noLogsAvailable and modulePage.phases.resyncMigrationPlanInstructions
 keys
• Reorganized translation structure for better grouping

workspaces/x2a/plugins/x2a/src/translations/ref.ts


9. workspaces/x2a/plugins/x2a-backend/src/services/KubeService.ts ✨ Enhancement +16/-4

Improve Kubernetes pod log retrieval with pending state check

• Added stringifyError utility import for better error logging
• Added check for pod phase Pending to avoid fetching logs from uninitialized pods
• Improved error handling with stringifyError for consistent error message formatting
• Enhanced pod readiness validation before attempting to fetch logs

workspaces/x2a/plugins/x2a-backend/src/services/KubeService.ts


10. workspaces/x2a/plugins/x2a/src/test-utils/projectListTestUtils.ts 🧪 Tests +85/-0

Add project list test utilities for reusable mock data

• New utility file for project list testing with reusable mock data generators
• Exports createMockProjects, createMockResponse, mockPermissionApi, and defaultTableProps
 functions
• Provides consistent test data structure for project list and table component tests

workspaces/x2a/plugins/x2a/src/test-utils/projectListTestUtils.ts


11. workspaces/x2a/plugins/x2a-backend/src/services/KubeService.test.ts 🧪 Tests +44/-0

Add tests for Kubernetes pod edge cases

• Added tests for pod with no name scenario
• Added tests for pod in Pending phase returning empty logs
• Added tests for Kubernetes API errors and list failures

workspaces/x2a/plugins/x2a-backend/src/services/KubeService.test.ts


12. workspaces/x2a/plugins/x2a-backend/src/schema/openapi/generated/router.ts Configuration +44/-0

Add OpenAPI spec for init phase log endpoint

• Added OpenAPI specification for new GET /projects/{projectId}/log endpoint
• Endpoint returns init phase logs with optional streaming query parameter
• Added initJob field to Project schema definition

workspaces/x2a/plugins/x2a-backend/src/schema/openapi/generated/router.ts


13. workspaces/x2a/plugins/x2a-common/client/src/schema/openapi/generated/apis/Api.client.ts ✨ Enhancement +39/-0

Add client API method for init phase logs

• Added ProjectsProjectIdLogGet type definition for init phase log endpoint
• Implemented projectsProjectIdLogGet method in DefaultApiClient for fetching project init logs
• Supports optional streaming query parameter

workspaces/x2a/plugins/x2a-common/client/src/schema/openapi/generated/apis/Api.client.ts


14. workspaces/x2a/plugins/x2a-common/src/utils/getScmProvider.test.ts 🧪 Tests +35/-0

Add tests for SCM provider detection utility

• New test file for getScmProvider utility function
• Tests cover GitHub and GitLab URL detection
• Tests include fallback behavior for other SCM providers (Bitbucket, Gitea)

workspaces/x2a/plugins/x2a-common/src/utils/getScmProvider.test.ts


15. workspaces/x2a/plugins/x2a/src/components/tools/buildRepoBranchUrl.ts ✨ Enhancement +40/-0

Add repository branch URL builder utility

• New utility function to build repository branch URLs for GitHub and GitLab
• Handles .git suffix removal and URL normalization
• Encodes branch names for special characters
• Gracefully handles invalid URLs

workspaces/x2a/plugins/x2a/src/components/tools/buildRepoBranchUrl.ts


16. workspaces/x2a/plugins/x2a-backend/src/utils/stringifyError.test.ts 🧪 Tests +39/-0

Add tests for error stringification utility

• New test file for stringifyError utility function
• Tests cover Error instances, objects with message property, plain objects, and primitives

workspaces/x2a/plugins/x2a-backend/src/utils/stringifyError.test.ts


17. workspaces/x2a/plugins/x2a-common/src/utils/getAuthTokenDescriptor.ts ✨ Enhancement +2/-3

Refactor to use SCM provider detection utility

• Refactored to use new getScmProvider utility function instead of inline GitHub URL check
• Improves code reusability and maintainability

workspaces/x2a/plugins/x2a-common/src/utils/getAuthTokenDescriptor.ts


18. workspaces/x2a/plugins/x2a-backend/src/utils/stringifyError.ts ✨ Enhancement +34/-0

Add error stringification utility function

• New utility function to convert unknown error values to strings suitable for logging
• Handles Error instances, objects with message property, plain objects, and primitives
• Avoids [object Object] string representation

workspaces/x2a/plugins/x2a-backend/src/utils/stringifyError.ts


19. workspaces/x2a/plugins/x2a-backend/src/schema/openapi/generated/apis/Api.server.ts Configuration +14/-0

Add server API type for init phase log endpoint

• Added ProjectsProjectIdLogGet type definition for server-side init phase log endpoint
• Added endpoint mapping for GET /projects/{projectId}/log

workspaces/x2a/plugins/x2a-backend/src/schema/openapi/generated/apis/Api.server.ts


20. workspaces/x2a/plugins/x2a/src/components/tools/getLastPhaseReached.ts ✨ Enhancement +32/-0

Add utility to get last phase reached by module

• New utility function to find the last job of the most advanced phase reached by a module
• Checks phases in order: publish, migrate, analyze

workspaces/x2a/plugins/x2a/src/components/tools/getLastPhaseReached.ts


21. workspaces/x2a/plugins/x2a-common/src/utils/getScmProvider.ts ✨ Enhancement +25/-0

Add SCM provider detection utility

• New utility function to detect SCM provider (GitHub or GitLab) from repository URL
• Based on presence of github.com in URL
• Defaults to GitLab for other providers

workspaces/x2a/plugins/x2a-common/src/utils/getScmProvider.ts


22. workspaces/x2a/plugins/x2a-backend/src/schema/openapi/generated/models/Project.model.ts ✨ Enhancement +2/-0

Add initJob field to Project model

• Added optional initJob field to Project model
• Imported Job model type

workspaces/x2a/plugins/x2a-backend/src/schema/openapi/generated/models/Project.model.ts


23. workspaces/x2a/plugins/x2a-common/client/src/schema/openapi/generated/models/Project.model.ts ✨ Enhancement +2/-0

Add initJob field to Project model

• Added optional initJob field to Project model
• Imported Job model type

workspaces/x2a/plugins/x2a-common/client/src/schema/openapi/generated/models/Project.model.ts


24. workspaces/x2a/plugins/x2a/src/test-utils/mockRouteRef.ts 🧪 Tests +23/-0

Add mock route reference utility for testing

• New test utility for mocking useRouteRef hook
• Provides mock implementation for x2a.project route reference

workspaces/x2a/plugins/x2a/src/test-utils/mockRouteRef.ts


25. workspaces/x2a/plugins/x2a-common/src/utils/index.ts ✨ Enhancement +1/-0

Export SCM provider detection utility

• Added export for new getScmProvider utility function

workspaces/x2a/plugins/x2a-common/src/utils/index.ts


26. workspaces/x2a/plugins/x2a-backend/src/utils/index.ts ✨ Enhancement +1/-0

Export error stringification utility

• Added export for new stringifyError utility function

workspaces/x2a/plugins/x2a-backend/src/utils/index.ts


27. workspaces/x2a/plugins/x2a/src/components/ProjectPage/index.ts ✨ Enhancement +17/-0

Add ProjectPage component export

• New index file exporting ProjectPage component

workspaces/x2a/plugins/x2a/src/components/ProjectPage/index.ts


28. workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/jobOperations.ts 🐞 Bug fix +3/-0

Filter init phase jobs to exclude module-specific jobs

• Added filter to ensure init phase jobs have module_id as null
• Prevents mixing module-specific jobs with project init jobs

workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/jobOperations.ts


29. workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/index.ts ✨ Enhancement +2/-0

Enrich project with init job information

• Added initJob field to project enrichment, populated with last init job (with sensitive data
 removed)

workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/index.ts


30. workspaces/x2a/plugins/x2a/src/routes.ts ✨ Enhancement +6/-0

Add project details page route reference

• Added new projectRouteRef for project details page route
• Route path: /projects/:projectId

workspaces/x2a/plugins/x2a/src/routes.ts


31. workspaces/x2a/plugins/x2a/src/components/tools/index.ts ✨ Enhancement +2/-0

Export new utility functions

• Added exports for buildRepoBranchUrl and getLastPhaseReached utilities

workspaces/x2a/plugins/x2a/src/components/tools/index.ts


32. workspaces/x2a/plugins/x2a/src/components/ModulePage/PhasesCard.tsx ✨ Enhancement +5/-250

Extract PhaseDetails component for reusability

• Extracted PhaseDetails component to separate file for reusability
• Removed inline phase details logic and helper functions
• Updated type signature to use MigrationPhase instead of ModulePhase

workspaces/x2a/plugins/x2a/src/components/ModulePage/PhasesCard.tsx


33. workspaces/x2a/plugins/x2a/src/components/PhaseDetails.tsx ✨ Enhancement +287/-0

Add reusable PhaseDetails component

• New reusable component for displaying phase details and logs
• Supports both module phases and init phase
• Includes log viewing with streaming support
• Handles phase-specific actions and instructions

workspaces/x2a/plugins/x2a/src/components/PhaseDetails.tsx


34. workspaces/x2a/plugins/x2a/report.api.md 📝 Documentation +20/-15

Update API report for new features

• Updated API report with new translation keys for project details page
• Added projectsProjectIdLogGet client method
• Added getScmProvider utility export
• Added initJob field to Project interface
• Removed deprecated wizard translation keys

workspaces/x2a/plugins/x2a/report.api.md


35. workspaces/x2a/plugins/x2a/src/components/ProjectList/ProjectTable.tsx +32/-9

Add project name links to details page

• Made project name clickable link to project details page
• Updated to use projectRouteRef for navigation
• Fixed total count display to use totalCount instead of projects.length
• Moved Repository and ProjectStatusCell imports to parent components directory
• Improved column memoization with proper dependency tracking

workspaces/x2a/plugins/x2a/src/components/ProjectList/ProjectTable.tsx


36. workspaces/x2a/plugins/x2a-backend/src/schema/openapi.yaml ✨ Enhancement +29/-0

OpenAPI schema updates for project init logs

• Added new /projects/{projectId}/log endpoint to retrieve init phase logs with optional streaming
 support
• Added initJob field to the Project schema to reference the project's init job

workspaces/x2a/plugins/x2a-backend/src/schema/openapi.yaml


37. workspaces/x2a/plugins/x2a/src/components/ProjectPage/InitPhaseCard.tsx ✨ Enhancement +35/-0

New InitPhaseCard component for project details

• New component displaying project initialization phase details using InfoCard and PhaseDetails
 components
• Integrates with translation system for localization
• Includes TODO comment for future phase resync functionality

workspaces/x2a/plugins/x2a/src/components/ProjectPage/InitPhaseCard.tsx


38. workspaces/x2a/plugins/x2a/src/components/Router.tsx ✨ Enhancement +3/-1

Router configuration for project details page

• Added import for projectRouteRef and new ProjectPage component
• Added new route for project details page using projectRouteRef.path

workspaces/x2a/plugins/x2a/src/components/Router.tsx


39. workspaces/x2a/plugins/x2a/src/components/ProjectList/DetailPanel.tsx Miscellaneous +1/-1

Import path refactoring for artifact components

• Moved ArtifactLink import from ModuleTable/Artifacts to shared Artifacts location
• Reorganized imports for better code structure

workspaces/x2a/plugins/x2a/src/components/ProjectList/DetailPanel.tsx


40. workspaces/x2a/plugins/x2a/src/components/ProjectStatusCell.tsx 🐞 Bug fix +1/-1

Import path correction for translation hook

• Fixed import path for useTranslation hook from relative to correct location

workspaces/x2a/plugins/x2a/src/components/ProjectStatusCell.tsx


41. workspaces/x2a/.changeset/eight-jokes-invent.md 📝 Documentation +8/-0

Changeset for project details page feature

• New changeset documenting patch version updates across four x2a packages
• Describes addition of Project Details Page and fixes to Module Details Page

workspaces/x2a/.changeset/eight-jokes-invent.md


42. workspaces/x2a/plugins/x2a/src/components/ModulePage/ModuleDetailsCard.tsx 🐞 Bug fix +1/-1

Translation key update for empty state

• Changed translation key from module.phases.none to generic empty for empty state display

workspaces/x2a/plugins/x2a/src/components/ModulePage/ModuleDetailsCard.tsx


43. workspaces/x2a/plugins/scaffolder-backend-module-x2a/src/actions/createProject.ts Additional files +0/-1

...

workspaces/x2a/plugins/scaffolder-backend-module-x2a/src/actions/createProject.ts


44. workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/jobs.test.ts Additional files +0/-2

...

workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/jobs.test.ts


45. workspaces/x2a/plugins/x2a-common/report.api.md Additional files +16/-0

...

workspaces/x2a/plugins/x2a-common/report.api.md


46. workspaces/x2a/plugins/x2a/src/components/Artifacts.tsx Additional files +5/-5

...

workspaces/x2a/plugins/x2a/src/components/Artifacts.tsx


47. workspaces/x2a/plugins/x2a/src/components/CurrentPhaseCell.tsx Additional files +9/-16

...

workspaces/x2a/plugins/x2a/src/components/CurrentPhaseCell.tsx


48. workspaces/x2a/plugins/x2a/src/components/ModulePage/ModulePage.tsx Additional files +14/-8

...

workspaces/x2a/plugins/x2a/src/components/ModulePage/ModulePage.tsx


49. workspaces/x2a/plugins/x2a/src/components/ModulePage/ModulePageBreadcrumb.tsx Additional files +15/-4

...

workspaces/x2a/plugins/x2a/src/components/ModulePage/ModulePageBreadcrumb.tsx


50. workspaces/x2a/plugins/x2a/src/components/ModuleStatusCell.tsx Additional files +70/-0

...

workspaces/x2a/plugins/x2a/src/components/ModuleStatusCell.tsx


51. workspaces/x2a/plugins/x2a/src/components/ModuleTable/ModuleTable.tsx Additional files +8/-21

...

workspaces/x2a/plugins/x2a/src/components/ModuleTable/ModuleTable.tsx


52. workspaces/x2a/plugins/x2a/src/components/ProjectList/ProjectList.test.tsx Additional files +10/-35

...

workspaces/x2a/plugins/x2a/src/components/ProjectList/ProjectList.test.tsx


53. workspaces/x2a/plugins/x2a/src/components/ProjectList/ProjectTable.test.tsx Additional files +10/-55

...

workspaces/x2a/plugins/x2a/src/components/ProjectList/ProjectTable.test.tsx


54. workspaces/x2a/plugins/x2a/src/components/ProjectPage/ProjectDetailsCard.tsx Additional files +117/-0

...

workspaces/x2a/plugins/x2a/src/components/ProjectPage/ProjectDetailsCard.tsx


55. workspaces/x2a/plugins/x2a/src/components/ProjectPage/ProjectModulesCard.tsx Additional files +78/-0

...

workspaces/x2a/plugins/x2a/src/components/ProjectPage/ProjectModulesCard.tsx


56. workspaces/x2a/plugins/x2a/src/components/ProjectPage/ProjectPage.tsx Additional files +113/-0

...

workspaces/x2a/plugins/x2a/src/components/ProjectPage/ProjectPage.tsx


57. workspaces/x2a/plugins/x2a/src/components/ProjectPage/ProjectPageBreadcrumb.tsx Additional files +22/-21

...

workspaces/x2a/plugins/x2a/src/components/ProjectPage/ProjectPageBreadcrumb.tsx


58. workspaces/x2a/plugins/x2a/src/components/Repository.tsx Additional files +11/-1

...

workspaces/x2a/plugins/x2a/src/components/Repository.tsx


Grey Divider

Qodo Logo

@rhdh-qodo-merge
Copy link

rhdh-qodo-merge bot commented Mar 5, 2026

Code Review by Qodo

🐞 Bugs (9) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. GitHub Enterprise misdetected 🐞 Bug ✓ Correctness ⭐ New
Description
getScmProvider classifies anything not containing github.com as gitlab, which breaks GitHub
Enterprise/custom GitHub host support already present in the codebase. This will generate incorrect
branch URLs (GitLab-style /-/tree/...) and can select the wrong auth token descriptor/scopes for
GitHub Enterprise repos.
Code

workspaces/x2a/plugins/x2a-common/src/utils/getScmProvider.ts[R21-24]

+export const getScmProvider = (repoUrl: string): 'github' | 'gitlab' => {
+  // Based on https://docs.github.com/en/enterprise-cloud@latest/admin/managing-your-enterprise-account/changing-the-url-for-your-enterprise
+  // GitHub URLs should always contain github.com.
+  return repoUrl.includes('github.com') ? 'github' : 'gitlab';
Evidence
The codebase explicitly supports custom GitHub hosts (e.g. github.example.com) when normalizing
RepoUrlPicker inputs; however, getScmProvider only recognizes GitHub when the URL contains
github.com. The new UI link building uses getScmProvider to decide between GitHub vs GitLab
branch URL formats, so custom GitHub hosts will get GitLab-style paths. Additionally, token
descriptor selection uses getScmProvider, so misclassification can lead to requesting GitLab
scopes for GitHub Enterprise repos.

workspaces/x2a/plugins/x2a-common/src/utils/getScmProvider.ts[21-25]
workspaces/x2a/plugins/x2a-common/src/utils/normalizeRepoUrl.test.ts[32-36]
workspaces/x2a/plugins/x2a/src/components/tools/buildRepoBranchUrl.ts[23-37]
workspaces/x2a/plugins/x2a/src/components/Repository.tsx[60-67]
workspaces/x2a/plugins/x2a-common/src/utils/getAuthTokenDescriptor.ts[24-46]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`getScmProvider` currently treats any repo URL that doesn&#x27;t contain `github.com` as `gitlab`. This misclassifies GitHub Enterprise/custom GitHub hosts (e.g. `github.example.com`) and causes:
- wrong repository branch links in the UI (GitLab-style `/-/tree/...` instead of GitHub `/tree/...`)
- wrong auth token descriptor/scopes selection (`read_repository`/`write_repository` instead of `repo`)

## Issue Context
The codebase already supports custom GitHub hosts via `normalizeRepoUrl`, so this misclassification is a real compatibility bug.

## Fix Focus Areas
- workspaces/x2a/plugins/x2a-common/src/utils/getScmProvider.ts[21-25]
- workspaces/x2a/plugins/x2a-common/src/utils/getScmProvider.test.ts[1-35]
- workspaces/x2a/plugins/x2a/src/components/tools/buildRepoBranchUrl.ts[23-37]

## Suggested approach
1. Update `getScmProvider` to parse the URL (or tolerate RepoUrlPicker-style inputs by prefixing `https://` similarly to `normalizeRepoUrl`).
2. Determine provider by hostname:
  - if hostname includes/endsWith `gitlab` or equals known GitLab domains -&gt; `gitlab`
  - else if hostname includes `github` OR is not recognized as GitLab -&gt; default to `github` (since treating unknown as GitLab breaks GitHub Enterprise links/scopes).
  - (Optional) consider an explicit allowlist/config hook later.
3. Add a unit test that asserts `getScmProvider(&#x27;https://github.example.com/org/repo.git&#x27;) === &#x27;github&#x27;`.
4. Ensure `buildRepoBranchUrl` produces `/tree/&lt;branch&gt;` for that URL.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Seeds test data 🐞 Bug ⛯ Reliability
Description
EmptyProjectList unconditionally calls useSeedTestData(), which creates projects/modules/jobs and
reloads the page. If merged, visiting the empty-state screen will mutate real environments and can
spam job runs/reloads.
Code

workspaces/x2a/plugins/x2a/src/components/ProjectList/EmptyProjectList.tsx[R74-76]

+  // DO NOT MERGE
+  useSeedTestData();
+
Evidence
The empty-state component always invokes the seeding hook, and the hook creates data via API calls
(projects/modules/runs) and forces a full page reload.

workspaces/x2a/plugins/x2a/src/components/ProjectList/EmptyProjectList.tsx[70-76]
workspaces/x2a/plugins/x2a/src/useSeedTestData.ts[26-56]
workspaces/x2a/plugins/x2a/src/useSeedTestData.ts[60-133]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`EmptyProjectList` currently calls `useSeedTestData()` unconditionally. That hook creates real projects/modules/jobs via API calls and then reloads the page, which will corrupt/mutate environments if merged.
### Issue Context
The hook itself is labeled “Never use in production.” The call site is in a user-facing component that will run in normal navigation.
### Fix Focus Areas
- Remove the seeding call and its import entirely, or guard it behind a strict dev-only condition that cannot be enabled in production builds.
- workspaces/x2a/plugins/x2a/src/components/ProjectList/EmptyProjectList.tsx[74-76]
- workspaces/x2a/plugins/x2a/src/useSeedTestData.ts[26-133]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. K8s log errors swallowed 🐞 Bug ⛯ Reliability
Description
KubeService.getJobLogs now catches all errors and returns '', hiding real failures
(auth/network/404/etc.) and making clients see “no logs” rather than an actionable error; it can
also lead to empty logs being persisted during reconciliation flows.
Code

workspaces/x2a/plugins/x2a-backend/src/services/KubeService.ts[R430-432]

+      // Might happen, i.e. the pod is in ContainerCreating state
+      this.#logger.warn(`Failed to get job logs: ${error.message}`);
+      return '';
Evidence
The new implementation returns an empty string for any exception, not just known-transient states.
The reconciliation path explicitly expects getJobLogs to throw (it has a try/catch) and will
update the DB with whatever string it receives; with the new behavior, the catch will never run and
an empty log can be stored, permanently losing observability for that job run.

workspaces/x2a/plugins/x2a-backend/src/services/KubeService.ts[401-433]
workspaces/x2a/plugins/x2a-backend/src/router/common.ts[293-330]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`KubeService.getJobLogs` currently swallows *all* errors and returns an empty string. This hides real failures from API clients and also breaks the intent of `reconcileJobStatus` which expects `getJobLogs` to throw on failure.
### Issue Context
Returning `&amp;#x27;&amp;#x27;` for every exception makes &amp;quot;no logs&amp;quot; indistinguishable from &amp;quot;couldn&amp;#x27;t fetch logs&amp;quot; and can cause empty logs to be written into the DB.
### Fix Focus Areas
- workspaces/x2a/plugins/x2a-backend/src/services/KubeService.ts[401-433]
- workspaces/x2a/plugins/x2a-backend/src/router/common.ts[312-326]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
4. Seeds test data 🐞 Bug ⛯ Reliability
Description
EmptyProjectList unconditionally calls useSeedTestData(), which creates projects/modules/jobs and
reloads the page. If merged, visiting the empty-state screen will mutate real environments and can
spam job runs/reloads.
Code

workspaces/x2a/plugins/x2a/src/components/ProjectList/EmptyProjectList.tsx[R74-76]

+  // DO NOT MERGE
+  useSeedTestData();
+
Evidence
The empty-state component always invokes the seeding hook, and the hook creates data via API calls
(projects/modules/runs) and forces a full page reload.

workspaces/x2a/plugins/x2a/src/components/ProjectList/EmptyProjectList.tsx[70-76]
workspaces/x2a/plugins/x2a/src/useSeedTestData.ts[26-56]
workspaces/x2a/plugins/x2a/src/useSeedTestData.ts[60-133]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`EmptyProjectList` currently calls `useSeedTestData()` unconditionally. That hook creates real projects/modules/jobs via API calls and then reloads the page, which will corrupt/mutate environments if merged.
### Issue Context
The hook itself is labeled “Never use in production.” The call site is in a user-facing component that will run in normal navigation.
### Fix Focus Areas
- Remove the seeding call and its import entirely, or guard it behind a strict dev-only condition that cannot be enabled in production builds.
- workspaces/x2a/plugins/x2a/src/components/ProjectList/EmptyProjectList.tsx[74-76]
- workspaces/x2a/plugins/x2a/src/useSeedTestData.ts[26-133]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

5. Init log job mismatch 🐞 Bug ✓ Correctness
Description
GET /projects/:projectId/log selects the latest job by phase='init' only, without guaranteeing it is
the project-level init job (module_id NULL). If module-scoped init jobs exist, this endpoint (and
Project.initJob enrichment) can return the wrong job/logs.
Code

workspaces/x2a/plugins/x2a-backend/src/router/jobs.ts[R59-64]

+    // Get latest init job
+    const jobs = await x2aDatabase.listJobs({
+      projectId,
+      phase: 'init',
+      lastJobOnly: true,
+    });
Evidence
The new endpoint queries init jobs without constraining module_id, while the data model/tests
demonstrate init jobs may have moduleId set. Separately, project init jobs are expected to have no
moduleId, and enrichProject now exposes initJob using the same unfiltered selection.

workspaces/x2a/plugins/x2a-backend/src/router/jobs.ts[59-64]
workspaces/x2a/plugins/x2a-backend/src/router/common.test.ts[90-100]
workspaces/x2a/plugins/x2a-backend/src/router/projects.ts[331-339]
workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/index.ts[68-85]
workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/jobOperations.ts[249-255]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The init-log endpoint and `Project.initJob` enrichment select the “latest init job” by `phase=&amp;#x27;init&amp;#x27;` only. If any init job exists with a `moduleId` set, the endpoint/enrichment can pick a module-scoped init job instead of the project-level init job.
### Issue Context
The codebase indicates project init jobs are expected to have no module (`moduleId: undefined`), but tests demonstrate init jobs can carry a moduleId. Today `listJobs()` cannot express `module_id IS NULL` filtering.
### Fix Focus Areas
- Add support in `listJobs`/`JobOperations.listJobs` for `moduleId: null` meaning `whereNull(&amp;#x27;module_id&amp;#x27;)`, and update the init log route + `enrichProject` to request `moduleId: null`.
- Alternatively, implement a dedicated DB operation to fetch the latest *project-level* init job.
- workspaces/x2a/plugins/x2a-backend/src/router/jobs.ts[59-64]
- workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/index.ts[68-85]
- workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/jobOperations.ts[234-280]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. ES i18n encoding broken 🐞 Bug ✓ Correctness
Description
The Spanish translation file contains corrupted characters (e.g., "conversi?n", "Descripci?n"),
which will render broken UI text for Spanish users. This looks like an encoding/regeneration issue
and should be corrected before release.
Code

workspaces/x2a/plugins/x2a/src/translations/es.ts[R27-31]

+    'sidebar.x2a.title': 'Hub de conversi?n',
+    'page.title': 'Hub de conversi?n',
   'page.subtitle':
-      'Inicie y realice el seguimiento de las conversiones asíncronas de archivos Chef a playbooks Ansible listos para producción.',
-    'page.devTitle': 'Hub de conversión',
+      'Inicie y realice el seguimiento de las conversiones as?ncronas de archivos Chef a playbooks Ansible listos para producci?n.',
   'table.columns.name': 'Nombre',
-    'table.columns.abbreviation': 'Abreviatura',
Evidence
The committed strings include replacement characters ('?') where accented characters should be
present, indicating text corruption in the source translations.

workspaces/x2a/plugins/x2a/src/translations/es.ts[24-31]
workspaces/x2a/plugins/x2a/src/translations/es.ts[45-49]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`translations/es.ts` contains corrupted Spanish strings with `?` replacement characters instead of accents (e.g., `conversión`, `Descripción`, `Módulos`). This will display broken UI text.
### Issue Context
Other locales appear fine; the issue is localized to the Spanish file and likely caused by an encoding or copy/paste/regeneration problem.
### Fix Focus Areas
- Replace corrupted strings with correct Spanish (UTF-8) equivalents.
- Ensure the file is saved/committed as UTF-8 and the generation pipeline preserves encoding.
- workspaces/x2a/plugins/x2a/src/translations/es.ts[24-80]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Init log route skips reconcile 🐞 Bug ✓ Correctness
Description
The new GET /projects/:projectId/log endpoint decides between DB vs K8s logs based on the DB job
status without reconciling job status against K8s first, so stale DB status can lead to querying K8s
for logs even when the job has actually completed (and pods may already be gone), returning empty
logs.
Code

workspaces/x2a/plugins/x2a-backend/src/router/jobs.ts[R72-109]

+    const latestJob = jobs[0]; // Already sorted by started_at DESC in listJobs
+
+    // If job is finished, return logs from database
+    if (latestJob.status === 'success' || latestJob.status === 'error') {
+      logger.info(
+        `Job ${latestJob.id} is finished (status: ${latestJob.status}), returning logs from database`,
+      );
+      res.setHeader('Content-Type', 'text/plain');
+      const log = await x2aDatabase.getJobLogs({ jobId: latestJob.id });
+      if (!log) {
+        logger.error(`Log not found for a finished job ${latestJob.id}`);
+      }
+      res.send(log || '');
+      return;
+    }
+
+    // Check if job has k8sJobName
+    if (!latestJob.k8sJobName) {
+      logger.warn(
+        `Job ${latestJob.id} has no k8sJobName, returning empty logs`,
+      );
+      res.setHeader('Content-Type', 'text/plain');
+      res.send('');
+      return;
+    }
+
+    // Get logs from Kubernetes
+    const logs = await kubeService.getJobLogs(latestJob.k8sJobName, streaming);
+
+    // Set content type
+    res.setHeader('Content-Type', 'text/plain');
+
+    // Handle streaming vs non-streaming
+    if (streaming && typeof logs !== 'string') {
+      logs.pipe(res);
+    } else {
+      res.send(logs as string);
+    }
Evidence
Other parts of the backend reconcile active init jobs to avoid acting on stale DB state.
reconcileJobStatus is documented as the only mechanism to sync stale DB job records with actual
K8s state, but the new init-log endpoint does not use it and directly uses latestJob.status to
choose DB vs K8s. Combined with getJobLogs returning '' on failures, this can surface as “empty
logs” even when the job is actually finished/failed.

workspaces/x2a/plugins/x2a-backend/src/router/jobs.ts[59-109]
workspaces/x2a/plugins/x2a-backend/src/router/common.ts[293-310]
workspaces/x2a/plugins/x2a-backend/src/router/projects.ts[307-320]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`GET /projects/:projectId/log` uses the DB job status directly to decide whether to return DB logs or fetch K8s logs. If the DB status is stale, the endpoint can fetch from K8s unnecessarily and return empty logs.
### Issue Context
There is an existing `reconcileJobStatus` helper intended to sync stale DB job records with K8s state.
### Fix Focus Areas
- workspaces/x2a/plugins/x2a-backend/src/router/jobs.ts[59-110]
- workspaces/x2a/plugins/x2a-backend/src/router/common.ts[293-334]
- workspaces/x2a/plugins/x2a-backend/src/services/KubeService.ts[401-433]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (2)
8. Init log job mismatch 🐞 Bug ✓ Correctness
Description
GET /projects/:projectId/log selects the latest job by phase='init' only, without guaranteeing it is
the project-level init job (module_id NULL). If module-scoped init jobs exist, this endpoint (and
Project.initJob enrichment) can return the wrong job/logs.
Code

workspaces/x2a/plugins/x2a-backend/src/router/jobs.ts[R59-64]

+    // Get latest init job
+    const jobs = await x2aDatabase.listJobs({
+      projectId,
+      phase: 'init',
+      lastJobOnly: true,
+    });
Evidence
The new endpoint queries init jobs without constraining module_id, while the data model/tests
demonstrate init jobs may have moduleId set. Separately, project init jobs are expected to have no
moduleId, and enrichProject now exposes initJob using the same unfiltered selection.

workspaces/x2a/plugins/x2a-backend/src/router/jobs.ts[59-64]
workspaces/x2a/plugins/x2a-backend/src/router/common.test.ts[90-100]
workspaces/x2a/plugins/x2a-backend/src/router/projects.ts[331-339]
workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/index.ts[68-85]
workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/jobOperations.ts[249-255]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The init-log endpoint and `Project.initJob` enrichment select the “latest init job” by `phase=&amp;amp;#x27;init&amp;amp;#x27;` only. If any init job exists with a `moduleId` set, the endpoint/enrichment can pick a module-scoped init job instead of the project-level init job.
### Issue Context
The codebase indicates project init jobs are expected to have no module (`moduleId: undefined`), but tests demonstrate init jobs can carry a moduleId. Today `listJobs()` cannot express `module_id IS NULL` filtering.
### Fix Focus Areas
- Add support in `listJobs`/`JobOperations.listJobs` for `moduleId: null` meaning `whereNull(&amp;amp;#x27;module_id&amp;amp;#x27;)`, and update the init log route + `enrichProject` to request `moduleId: null`.
- Alternatively, implement a dedicated DB operation to fetch the latest *project-level* init job.
- workspaces/x2a/plugins/x2a-backend/src/router/jobs.ts[59-64]
- workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/index.ts[68-85]
- workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/jobOperations.ts[234-280]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. ES i18n encoding broken 🐞 Bug ✓ Correctness
Description
The Spanish translation file contains corrupted characters (e.g., "conversi?n", "Descripci?n"),
which will render broken UI text for Spanish users. This looks like an encoding/regeneration issue
and should be corrected before release.
Code

workspaces/x2a/plugins/x2a/src/translations/es.ts[R27-31]

+    'sidebar.x2a.title': 'Hub de conversi?n',
+    'page.title': 'Hub de conversi?n',
  'page.subtitle':
-      'Inicie y realice el seguimiento de las conversiones asíncronas de archivos Chef a playbooks Ansible listos para producción.',
-    'page.devTitle': 'Hub de conversión',
+      'Inicie y realice el seguimiento de las conversiones as?ncronas de archivos Chef a playbooks Ansible listos para producci?n.',
  'table.columns.name': 'Nombre',
-    'table.columns.abbreviation': 'Abreviatura',
Evidence
The committed strings include replacement characters ('?') where accented characters should be
present, indicating text corruption in the source translations.

workspaces/x2a/plugins/x2a/src/translations/es.ts[24-31]
workspaces/x2a/plugins/x2a/src/translations/es.ts[45-49]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`translations/es.ts` contains corrupted Spanish strings with `?` replacement characters instead of accents (e.g., `conversión`, `Descripción`, `Módulos`). This will display broken UI text.
### Issue Context
Other locales appear fine; the issue is localized to the Spanish file and likely caused by an encoding or copy/paste/regeneration problem.
### Fix Focus Areas
- Replace corrupted strings with correct Spanish (UTF-8) equivalents.
- Ensure the file is saved/committed as UTF-8 and the generation pipeline preserves encoding.
- workspaces/x2a/plugins/x2a/src/translations/es.ts[24-80]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines +21 to +24
export const getScmProvider = (repoUrl: string): 'github' | 'gitlab' => {
// Based on https://docs.github.com/en/enterprise-cloud@latest/admin/managing-your-enterprise-account/changing-the-url-for-your-enterprise
// GitHub URLs should always contain github.com.
return repoUrl.includes('github.com') ? 'github' : 'gitlab';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Github enterprise misdetected 🐞 Bug ✓ Correctness

getScmProvider classifies anything not containing github.com as gitlab, which breaks GitHub
Enterprise/custom GitHub host support already present in the codebase. This will generate incorrect
branch URLs (GitLab-style /-/tree/...) and can select the wrong auth token descriptor/scopes for
GitHub Enterprise repos.
Agent Prompt
## Issue description
`getScmProvider` currently treats any repo URL that doesn't contain `github.com` as `gitlab`. This misclassifies GitHub Enterprise/custom GitHub hosts (e.g. `github.example.com`) and causes:
- wrong repository branch links in the UI (GitLab-style `/-/tree/...` instead of GitHub `/tree/...`)
- wrong auth token descriptor/scopes selection (`read_repository`/`write_repository` instead of `repo`)

## Issue Context
The codebase already supports custom GitHub hosts via `normalizeRepoUrl`, so this misclassification is a real compatibility bug.

## Fix Focus Areas
- workspaces/x2a/plugins/x2a-common/src/utils/getScmProvider.ts[21-25]
- workspaces/x2a/plugins/x2a-common/src/utils/getScmProvider.test.ts[1-35]
- workspaces/x2a/plugins/x2a/src/components/tools/buildRepoBranchUrl.ts[23-37]

## Suggested approach
1. Update `getScmProvider` to parse the URL (or tolerate RepoUrlPicker-style inputs by prefixing `https://` similarly to `normalizeRepoUrl`).
2. Determine provider by hostname:
   - if hostname includes/endsWith `gitlab` or equals known GitLab domains -> `gitlab`
   - else if hostname includes `github` OR is not recognized as GitLab -> default to `github` (since treating unknown as GitLab breaks GitHub Enterprise links/scopes).
   - (Optional) consider an explicit allowlist/config hook later.
3. Add a unit test that asserts `getScmProvider('https://github.example.com/org/repo.git') === 'github'`.
4. Ensure `buildRepoBranchUrl` produces `/tree/<branch>` for that URL.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Member Author

@mareklibra mareklibra Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's address that in a separate PR (FLPATH-3398). Additional data (on top of the URL) will need to be stored.

- Handle stream errors in job log endpoint
- Add sendJobLogs helper, KubeService edge-case handling
- Add buildRepoBranchUrl for clickable repo links
- Rename getLastJob to getLastPhaseReached
@mareklibra mareklibra force-pushed the FLPATH-3143.projectDetailPage branch from 8ebe6e2 to 98e7f1d Compare March 5, 2026 14:42
@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 5, 2026

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants