Skip to content

EDM-3516: Catalog landing page#571

Open
rawagner wants to merge 1 commit intoflightctl:mainfrom
rawagner:catalog_empty
Open

EDM-3516: Catalog landing page#571
rawagner wants to merge 1 commit intoflightctl:mainfrom
rawagner:catalog_empty

Conversation

@rawagner
Copy link
Collaborator

@rawagner rawagner commented Mar 13, 2026

Summary by CodeRabbit

  • New Features

    • Software Catalog landing page with collapsible onboarding (Build custom images, Create item, Import catalog, Learn more).
    • New "Create catalog item" button surfaced in catalog toolbar.
  • Improvements

    • Enhanced catalog page: loading, error handling, and improved empty-state UX with contextual actions.
    • Permission-aware controls and tooltips for catalog actions.
    • Updated English translations: many new strings added and duplicate/obsolete labels removed.
    • Repository creation: catalog-sync flow and more permissive path validation.
    • Navigation now supports optional URL query parameters.

@coderabbitai
Copy link

coderabbitai bot commented Mar 13, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a Catalog landing UI and styles, refactors catalog listing into a fetch-and-render wrapper with loading/error/empty handling, extracts an RBAC-aware create-item button, updates repository creation for catalog-sync, extends navigation to accept URL params, updates app links and RBAC types, and introduces English translation keys.

Changes

Cohort / File(s) Summary
Catalog Landing Page
libs/ui-components/src/components/Catalog/CatalogLandingPage.tsx, libs/ui-components/src/components/Catalog/CatalogLandingPage.css
New CatalogLandingPage component + CSS: collapsible card sections, permission-aware actions/buttons, informational alert, responsive grid, navigation to image/repo/item routes and docs.
Catalog Page Refactor
libs/ui-components/src/components/Catalog/CatalogPage.tsx, libs/ui-components/src/components/Catalog/ResourceCatalogPage.tsx
Adds CatalogPageWithInit wrapper that fetches catalogs and handles loading/error/empty states; CatalogPageContent made internal and receives catalogs/refetchCatalogs; ResourceCatalogPage now mounts the wrapper.
Catalog Toolbar / Create Button
libs/ui-components/src/components/Catalog/CatalogPageToolbar.tsx
Extracts and exports CreateCatalogItemBtn enforcing RBAC (RESOURCE.CATALOG_ITEM, VERB.CREATE) and responsible for navigation; toolbar delegates creation UI to this component.
Repository — Catalog Sync Flow
libs/ui-components/src/components/Repository/CreateRepository/CreateRepository.tsx, libs/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx, libs/ui-components/src/components/Repository/CreateRepository/utils.ts
Adds isCatalogSync URL mode to constrain repo type and resource-sync types, introduces mustUseResourceSyncs/allowedRSTypes options, adjusts onClose/onSuccess navigation for catalog-sync, and relaxes repo path regex to allow /.
Formik resource-syncs
libs/ui-components/src/components/Repository/CreateRepository/CreateResourceSyncsForm.tsx
Adds Formik useField, passes allowedRSTypes through child forms, auto-resets disallowed types, and disables radio options when not allowed.
Navigation Hook
libs/ui-components/src/hooks/useNavigate.tsx
Extends NavigateFunction to accept optional urlParams?: Record<string,string> and appends them as a query string to navigated routes (supports both Route and ToObj).
App Links
libs/ui-components/src/hooks/useAppLinks.ts
Adds catalog to AppLink type and to links mapping (fc.catalog populated; acm.catalog and aap.catalog added as empty strings).
RBAC Types
libs/ui-components/src/types/rbac.ts
Adds RESOURCE.CATALOG_ITEM = 'catalogitems' to the exported RBAC enum.
Translations
libs/i18n/locales/en/translation.json
Adds ~30 new English translation keys for Software Catalog onboarding/workflows and removes several duplicate/deprecated keys (net +30/-4 lines).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'EDM-3516: Catalog landing page' directly corresponds to the main change - a new CatalogLandingPage component with associated styling, translations, and supporting infrastructure.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rawagner
Copy link
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Mar 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
libs/ui-components/src/components/Repository/CreateRepository/CreateResourceSyncsForm.tsx (1)

124-124: ⚠️ Potential issue | 🟡 Minor

Missing type field when adding a new resource sync.

When a new resource sync is pushed, the type field is omitted. While the useEffect (lines 28-32) will reset it if allowedRSTypes is defined, if allowedRSTypes is undefined, the field remains unset, causing inconsistency with the initial values in utils.ts which defaults to ResourceSyncType.ResourceSyncTypeFleet.

🔧 Proposed fix
-            onClick={() => arrayHelpers.push({ name: '', path: '', targetRevision: '' } as ResourceSyncFormValue)}
+            onClick={() => arrayHelpers.push({ name: '', path: '', targetRevision: '', type: ResourceSyncType.ResourceSyncTypeFleet } as ResourceSyncFormValue)}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@libs/ui-components/src/components/Repository/CreateRepository/CreateResourceSyncsForm.tsx`
at line 124, The onClick push in CreateResourceSyncsForm is adding { name, path,
targetRevision } without the required type, causing inconsistent defaults;
update the arrayHelpers.push call in CreateResourceSyncsForm to include a
default type (e.g., ResourceSyncType.ResourceSyncTypeFleet or the same default
used in utils.ts) so new ResourceSyncFormValue entries always have a type field,
and ensure the symbol allowedRSTypes/useEffect behavior remains unchanged.
🧹 Nitpick comments (3)
libs/ui-components/src/components/Repository/CreateRepository/CreateRepository.tsx (1)

38-39: Redundant double negation.

isCatalogSync is already a boolean (line 39). The !! on line 114 is unnecessary.

✏️ Suggested simplification
-    if (!!isCatalogSync) {
+    if (isCatalogSync) {

Also applies to: 114-118

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@libs/ui-components/src/components/Repository/CreateRepository/CreateRepository.tsx`
around lines 38 - 39, The variable isCatalogSync (created via useSearchParams in
CreateRepository) is already a boolean, so remove redundant double-negation
usages: keep a single boolean conversion at the source (either const
isCatalogSync = !!searchParams.get('isCatalogSync') or const isCatalogSync =
Boolean(searchParams.get('isCatalogSync'))) and remove any additional
`!!isCatalogSync` occurrences later in the file; search for and replace
references that use `!!isCatalogSync` to use `isCatalogSync` directly (affecting
code paths that reference isCatalogSync around the CreateRepository component).
libs/ui-components/src/components/Catalog/CatalogPageToolbar.tsx (1)

16-28: Consider returning null instead of false from component render.

When canCreate is false, line 22 returns false which can cause React warnings in some contexts. Prefer returning null explicitly for cleaner component output.

♻️ Suggested fix
 export const CreateCatalogItemBtn = () => {
   const { checkPermissions } = usePermissionsContext();
   const [canCreate] = checkPermissions(permissions);
   const { t } = useTranslation();
   const navigate = useNavigate();
+
+  if (!canCreate) {
+    return null;
+  }
+
   return (
-    !!canCreate && (
-      <Button variant="primary" onClick={() => navigate(ROUTE.CATALOG_ADD_ITEM)}>
-        {t('Create item')}
-      </Button>
-    )
+    <Button variant="primary" onClick={() => navigate(ROUTE.CATALOG_ADD_ITEM)}>
+      {t('Create item')}
+    </Button>
   );
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/ui-components/src/components/Catalog/CatalogPageToolbar.tsx` around
lines 16 - 28, The CreateCatalogItemBtn component currently returns a boolean
when the user lacks permission (via the canCreate value from checkPermissions),
which can produce React warnings; update the render logic in
CreateCatalogItemBtn to explicitly return null when !canCreate (e.g., replace
the &&-expression with a conditional that returns the Button when canCreate is
true and null otherwise), keeping the usePermissionsContext().checkPermissions
and navigate/t translation usage unchanged.
libs/ui-components/src/components/Catalog/CatalogLandingPage.tsx (1)

52-52: Consider adding keyboard accessibility to CardHeader.

The CardHeader has onClick and role="button" but lacks keyboard event handlers (onKeyDown/onKeyUp for Enter/Space). This could affect keyboard navigation.

♿ Suggested enhancement
-      <CardHeader onClick={onExpand} role="button" className="fctl-catalog-landing__header">
+      <CardHeader
+        onClick={onExpand}
+        onKeyDown={(e) => {
+          if (e.key === 'Enter' || e.key === ' ') {
+            e.preventDefault();
+            onExpand();
+          }
+        }}
+        role="button"
+        tabIndex={0}
+        className="fctl-catalog-landing__header"
+      >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/ui-components/src/components/Catalog/CatalogLandingPage.tsx` at line 52,
The CardHeader element currently has onClick and role="button" but is missing
keyboard accessibility; update the CardHeader (fctl-catalog-landing__header) to
be focusable (add tabIndex={0}) and add an onKeyDown handler that calls the same
onExpand function when Enter or Space is pressed (handle Space by preventing
default to avoid page scroll), ensuring the keyboard activation mirrors the
onClick behavior while keeping role="button".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@libs/i18n/locales/en/translation.json`:
- Line 310: The translation string in CatalogLandingPage.tsx contains a typo
("You dont" instead of "You don't") which caused incorrect entries in
libs/i18n/locales/en/translation.json; open CatalogLandingPage.tsx, correct the
three source strings (the instances producing the keys around "You dont have
permissions to create image builds" and the two other occurrences noted), then
regenerate the localization bundle by running npm run i18n so the corrected keys
are emitted into libs/i18n/locales/en/translation.json rather than editing that
JSON file directly.

In `@libs/ui-components/src/components/Catalog/CatalogLandingPage.tsx`:
- Line 140: In CatalogLandingPage (component CatalogLandingPage) fix the typo in
the tooltip translation keys used in the content={t('...')} calls by changing
"You dont have permissions to create image builds" to "You don't have
permissions to create image builds" (and apply the same change to the other two
content={t('...')} occurrences in this file); ensure the apostrophe is correctly
escaped or switch to double-quoted string for the t(...) literal so JSX/string
quoting is valid.
- Line 107: The Card is using the setter function instead of the state value
(isExpanded={!!setExpandedItem}), so replace that with the correct state or
remove the prop: either change isExpanded to use the expansion state variable
(e.g., isExpanded={!!expandedItem} or isExpanded={expandedItem ===
'build-images'} if you track per-card keys) or drop the Card's isExpanded prop
entirely and let CardSection manage expansion; locate the Card with id
"build-images-card" and update the isExpanded usage and any related logic that
reads/writes setExpandedItem accordingly.

In
`@libs/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx`:
- Around line 434-440: getInitValues currently doesn't accept
mustUseResourceSyncs so values.useResourceSyncs can be false even when the
checkbox is shown checked via options?.mustUseResourceSyncs; update the
getInitValues signature in utils.ts to include mustUseResourceSyncs in its
options type and ensure the returned initial state sets useResourceSyncs: true
when options.mustUseResourceSyncs is true (otherwise fall back to existing
canUseResourceSyncs/canUseRSs logic), so the form value matches the disabled
checked checkbox used in CreateRepositoryForm (where isChecked/onChange
reference values.useResourceSyncs and options?.mustUseResourceSyncs).

---

Outside diff comments:
In
`@libs/ui-components/src/components/Repository/CreateRepository/CreateResourceSyncsForm.tsx`:
- Line 124: The onClick push in CreateResourceSyncsForm is adding { name, path,
targetRevision } without the required type, causing inconsistent defaults;
update the arrayHelpers.push call in CreateResourceSyncsForm to include a
default type (e.g., ResourceSyncType.ResourceSyncTypeFleet or the same default
used in utils.ts) so new ResourceSyncFormValue entries always have a type field,
and ensure the symbol allowedRSTypes/useEffect behavior remains unchanged.

---

Nitpick comments:
In `@libs/ui-components/src/components/Catalog/CatalogLandingPage.tsx`:
- Line 52: The CardHeader element currently has onClick and role="button" but is
missing keyboard accessibility; update the CardHeader
(fctl-catalog-landing__header) to be focusable (add tabIndex={0}) and add an
onKeyDown handler that calls the same onExpand function when Enter or Space is
pressed (handle Space by preventing default to avoid page scroll), ensuring the
keyboard activation mirrors the onClick behavior while keeping role="button".

In `@libs/ui-components/src/components/Catalog/CatalogPageToolbar.tsx`:
- Around line 16-28: The CreateCatalogItemBtn component currently returns a
boolean when the user lacks permission (via the canCreate value from
checkPermissions), which can produce React warnings; update the render logic in
CreateCatalogItemBtn to explicitly return null when !canCreate (e.g., replace
the &&-expression with a conditional that returns the Button when canCreate is
true and null otherwise), keeping the usePermissionsContext().checkPermissions
and navigate/t translation usage unchanged.

In
`@libs/ui-components/src/components/Repository/CreateRepository/CreateRepository.tsx`:
- Around line 38-39: The variable isCatalogSync (created via useSearchParams in
CreateRepository) is already a boolean, so remove redundant double-negation
usages: keep a single boolean conversion at the source (either const
isCatalogSync = !!searchParams.get('isCatalogSync') or const isCatalogSync =
Boolean(searchParams.get('isCatalogSync'))) and remove any additional
`!!isCatalogSync` occurrences later in the file; search for and replace
references that use `!!isCatalogSync` to use `isCatalogSync` directly (affecting
code paths that reference isCatalogSync around the CreateRepository component).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c1a2c13a-5077-4a9a-9165-8cef56749a83

📥 Commits

Reviewing files that changed from the base of the PR and between 0155317 and 6ef82a9.

📒 Files selected for processing (11)
  • libs/i18n/locales/en/translation.json
  • libs/ui-components/src/components/Catalog/CatalogLandingPage.css
  • libs/ui-components/src/components/Catalog/CatalogLandingPage.tsx
  • libs/ui-components/src/components/Catalog/CatalogPage.tsx
  • libs/ui-components/src/components/Catalog/CatalogPageToolbar.tsx
  • libs/ui-components/src/components/Catalog/ResourceCatalog/ResourceCatalogPage.tsx
  • libs/ui-components/src/components/Repository/CreateRepository/CreateRepository.tsx
  • libs/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx
  • libs/ui-components/src/components/Repository/CreateRepository/CreateResourceSyncsForm.tsx
  • libs/ui-components/src/hooks/useNavigate.tsx
  • libs/ui-components/src/types/rbac.ts

Comment on lines +434 to +440
isChecked={options?.mustUseResourceSyncs ? true : values.useResourceSyncs}
isDisabled={!!options?.mustUseResourceSyncs}
onChange={(_, checked) => {
// Trigger validation of the resource syncs items
return setFieldValue('useResourceSyncs', checked, true);
}}
body={values.useResourceSyncs && <CreateResourceSyncsForm />}
body={values.useResourceSyncs && <CreateResourceSyncsForm allowedRSTypes={options?.allowedRSTypes} />}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify how useResourceSyncs initial value is set when mustUseResourceSyncs is true
# Check if getInitValues handles this case

ast-grep --pattern $'export const getInitValues = ($$$) => {
  $$$
}'

# Also check if mustUseResourceSyncs is passed to getInitValues
rg -n "getInitValues" --type ts -A 5

Repository: flightctl/flightctl-ui

Length of output: 5736


🏁 Script executed:

cat -n libs/ui-components/src/components/Repository/CreateRepository/utils.ts | sed -n '461,550p'

Repository: flightctl/flightctl-ui

Length of output: 3889


🏁 Script executed:

cat -n libs/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx | sed -n '430,500p'

Repository: flightctl/flightctl-ui

Length of output: 3029


🏁 Script executed:

cat -n libs/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx | sed -n '495,550p'

Repository: flightctl/flightctl-ui

Length of output: 3117


Mismatch between checkbox display and form submission: getInitValues doesn't initialize useResourceSyncs when mustUseResourceSyncs is true.

When options?.mustUseResourceSyncs is true, the checkbox displays as checked and becomes disabled (lines 434–435), but the underlying form value useResourceSyncs is not initialized to true. The getInitValues function in utils.ts (line 461) only accepts canUseResourceSyncs, allowedRepoTypes, showRepoTypes, and writeAccessOnly in its options type—it lacks mustUseResourceSyncs. As a result, useResourceSyncs is initialized based only on canUseRSs logic (lines 492 and 527 in utils.ts), which may be false. During form submission (lines 500 and 543), only values.useResourceSyncs is checked, so resource syncs won't be created despite the checkbox indicating they should be.

Update getInitValues to accept mustUseResourceSyncs in its options type and set useResourceSyncs: true when this option is provided.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@libs/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx`
around lines 434 - 440, getInitValues currently doesn't accept
mustUseResourceSyncs so values.useResourceSyncs can be false even when the
checkbox is shown checked via options?.mustUseResourceSyncs; update the
getInitValues signature in utils.ts to include mustUseResourceSyncs in its
options type and ensure the returned initial state sets useResourceSyncs: true
when options.mustUseResourceSyncs is true (otherwise fall back to existing
canUseResourceSyncs/canUseRSs logic), so the form value matches the disabled
checked checkbox used in CreateRepositoryForm (where isChecked/onChange
reference values.useResourceSyncs and options?.mustUseResourceSyncs).

@rawagner rawagner force-pushed the catalog_empty branch 4 times, most recently from e33efe7 to 61832d3 Compare March 16, 2026 09:43
@rawagner rawagner marked this pull request as ready for review March 16, 2026 09:54
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
libs/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx (1)

434-440: ⚠️ Potential issue | 🟠 Major

Mismatch between checkbox display and form submission persists.

When options?.mustUseResourceSyncs is true, the checkbox displays as checked (line 434) and disabled (line 435), but the form's values.useResourceSyncs may still be false if getInitValues doesn't account for this option.

During submission (lines 500 and 543), only values.useResourceSyncs is checked:

if (values.useResourceSyncs) {
  // create resource syncs
}

This means resource syncs won't be created despite the checkbox indicating they should be.

#!/bin/bash
# Verify if getInitValues handles mustUseResourceSyncs
rg -n "mustUseResourceSyncs" libs/ui-components/src/components/Repository/CreateRepository/utils.ts -A 5 -B 2

# Check getInitValues function signature and logic
ast-grep --pattern $'export const getInitValues = ($$$) => {
  $$$
}'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@libs/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx`
around lines 434 - 440, The checkbox in CreateRepositoryForm shows
checked/disabled when options?.mustUseResourceSyncs is true but form state may
not reflect that, so update getInitValues (in CreateRepositoryForm utilities) to
set useResourceSyncs: true when options?.mustUseResourceSyncs is true (or ensure
the form's initialValues passed into CreateRepositoryForm include
useResourceSyncs=true), and in the submission paths that check
values.useResourceSyncs (the resource-sync creation logic) rely on that value or
alternatively read options?.mustUseResourceSyncs as a fallback (e.g., treat
useResourceSyncs as values.useResourceSyncs || options?.mustUseResourceSyncs) so
resource syncs are created when the checkbox is forced on; also ensure
setFieldValue logic that toggles useResourceSyncs remains consistent with this
initialization.
🧹 Nitpick comments (1)
libs/ui-components/src/components/Repository/CreateRepository/utils.ts (1)

30-30: Consider using greedy quantifier for clarity.

The non-greedy quantifier *? is unusual here. Since Yup's .matches() only checks for pattern existence (not capture groups), the non-greedy behavior has no effect. Using /\/.*/ would be the conventional idiom and clearer to maintainers.

Suggested change
-const pathRegex = /\/.*?/;
+const pathRegex = /\/.*/;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/ui-components/src/components/Repository/CreateRepository/utils.ts` at
line 30, The regex assigned to pathRegex uses a non-greedy quantifier "/\/.*?/"
which is unnecessary for Yup.matches() usage; update the pattern used in
pathRegex to the conventional greedy form "/\/.*/" so the intent is clearer to
maintainers and behavior is unchanged (look for a leading slash followed by any
characters); ensure any uses of pathRegex in the CreateRepository validation
remain unchanged otherwise.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@libs/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx`:
- Around line 434-440: The checkbox in CreateRepositoryForm shows
checked/disabled when options?.mustUseResourceSyncs is true but form state may
not reflect that, so update getInitValues (in CreateRepositoryForm utilities) to
set useResourceSyncs: true when options?.mustUseResourceSyncs is true (or ensure
the form's initialValues passed into CreateRepositoryForm include
useResourceSyncs=true), and in the submission paths that check
values.useResourceSyncs (the resource-sync creation logic) rely on that value or
alternatively read options?.mustUseResourceSyncs as a fallback (e.g., treat
useResourceSyncs as values.useResourceSyncs || options?.mustUseResourceSyncs) so
resource syncs are created when the checkbox is forced on; also ensure
setFieldValue logic that toggles useResourceSyncs remains consistent with this
initialization.

---

Nitpick comments:
In `@libs/ui-components/src/components/Repository/CreateRepository/utils.ts`:
- Line 30: The regex assigned to pathRegex uses a non-greedy quantifier
"/\/.*?/" which is unnecessary for Yup.matches() usage; update the pattern used
in pathRegex to the conventional greedy form "/\/.*/" so the intent is clearer
to maintainers and behavior is unchanged (look for a leading slash followed by
any characters); ensure any uses of pathRegex in the CreateRepository validation
remain unchanged otherwise.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1f044822-64e2-4a31-aca8-23b599caa3f1

📥 Commits

Reviewing files that changed from the base of the PR and between 6ef82a9 and 61832d3.

📒 Files selected for processing (12)
  • libs/i18n/locales/en/translation.json
  • libs/ui-components/src/components/Catalog/CatalogLandingPage.css
  • libs/ui-components/src/components/Catalog/CatalogLandingPage.tsx
  • libs/ui-components/src/components/Catalog/CatalogPage.tsx
  • libs/ui-components/src/components/Catalog/CatalogPageToolbar.tsx
  • libs/ui-components/src/components/Catalog/ResourceCatalog/ResourceCatalogPage.tsx
  • libs/ui-components/src/components/Repository/CreateRepository/CreateRepository.tsx
  • libs/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx
  • libs/ui-components/src/components/Repository/CreateRepository/CreateResourceSyncsForm.tsx
  • libs/ui-components/src/components/Repository/CreateRepository/utils.ts
  • libs/ui-components/src/hooks/useNavigate.tsx
  • libs/ui-components/src/types/rbac.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • libs/ui-components/src/hooks/useNavigate.tsx
  • libs/ui-components/src/types/rbac.ts
  • libs/ui-components/src/components/Repository/CreateRepository/CreateRepository.tsx
  • libs/ui-components/src/components/Catalog/CatalogLandingPage.css
  • libs/ui-components/src/components/Catalog/ResourceCatalog/ResourceCatalogPage.tsx

@rawagner rawagner changed the title Catalog landing page EDM-3516: Catalog landing page Mar 16, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
libs/ui-components/src/components/Repository/CreateRepository/CreateRepository.tsx (1)

114-114: Minor: Redundant double negation.

isCatalogSync is already a boolean from line 39 (!!searchParams.get(...)), so !!isCatalogSync is redundant.

♻️ Suggested fix
-    if (!!isCatalogSync) {
+    if (isCatalogSync) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@libs/ui-components/src/components/Repository/CreateRepository/CreateRepository.tsx`
at line 114, The condition uses a redundant double negation: replace the `if
(!!isCatalogSync)` check with a simple `if (isCatalogSync)` inside
CreateRepository (remove the `!!`), referencing the `isCatalogSync` variable
initialized from `searchParams.get(...)` so the boolean check is direct and
clearer.
libs/ui-components/src/components/Catalog/CatalogPage.tsx (1)

272-278: Consider removing optional chaining on metadata.name for API-returned catalogs.

Per coding guidelines, resource.metadata.name should be treated as always defined for resources returned from the API. The || '' fallbacks are overly defensive here since catalogs comes from the API response.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/ui-components/src/components/Catalog/CatalogPage.tsx` around lines 272 -
278, The code is overly defensive with optional fallbacks for catalog names; in
the catalogs.map block (referencing catalogs.map, canDelete, and the returned
object fields name, id, and checkProps) replace uses of `c.metadata.name || ''`
and `!!c.metadata.name` with direct accesses to `c.metadata.name` (e.g., name:
c.spec.displayName || c.metadata.name, id: c.metadata.name, and use
c.metadata.name when building checkProps.checked and catalogFilter.includes
checks) since API-returned resources guarantee metadata.name; ensure
types/signatures reflect non-null if needed.
libs/ui-components/src/components/Repository/CreateRepository/CreateResourceSyncsForm.tsx (1)

124-124: Consider setting a default type when adding a new resource sync.

When a new resource sync is added, no type is specified. The useEffect handles this when allowedRSTypes is provided, but when it's undefined, the new item has no type until user interaction.

♻️ Suggested improvement
-            onClick={() => arrayHelpers.push({ name: '', path: '', targetRevision: '' } as ResourceSyncFormValue)}
+            onClick={() => arrayHelpers.push({ name: '', path: '', targetRevision: '', type: allowedRSTypes?.[0] ?? ResourceSyncType.ResourceSyncTypeFleet } as ResourceSyncFormValue)}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@libs/ui-components/src/components/Repository/CreateRepository/CreateResourceSyncsForm.tsx`
at line 124, When pushing a new ResourceSyncFormValue via arrayHelpers.push,
include a default type so the item isn't created typeless; change the push
payload in CreateResourceSyncsForm (the arrayHelpers.push call) to set type:
allowedRSTypes?.[0] ?? defaultResourceSyncType (or a sensible constant you
define), and add a small defaultResourceSyncType constant near the component so
when allowedRSTypes is undefined the new item still has a valid type; this
touches ResourceSyncFormValue, the arrayHelpers.push call, and uses
allowedRSTypes and useEffect for consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@libs/ui-components/src/components/Catalog/CatalogPage.tsx`:
- Around line 272-278: The code is overly defensive with optional fallbacks for
catalog names; in the catalogs.map block (referencing catalogs.map, canDelete,
and the returned object fields name, id, and checkProps) replace uses of
`c.metadata.name || ''` and `!!c.metadata.name` with direct accesses to
`c.metadata.name` (e.g., name: c.spec.displayName || c.metadata.name, id:
c.metadata.name, and use c.metadata.name when building checkProps.checked and
catalogFilter.includes checks) since API-returned resources guarantee
metadata.name; ensure types/signatures reflect non-null if needed.

In
`@libs/ui-components/src/components/Repository/CreateRepository/CreateRepository.tsx`:
- Line 114: The condition uses a redundant double negation: replace the `if
(!!isCatalogSync)` check with a simple `if (isCatalogSync)` inside
CreateRepository (remove the `!!`), referencing the `isCatalogSync` variable
initialized from `searchParams.get(...)` so the boolean check is direct and
clearer.

In
`@libs/ui-components/src/components/Repository/CreateRepository/CreateResourceSyncsForm.tsx`:
- Line 124: When pushing a new ResourceSyncFormValue via arrayHelpers.push,
include a default type so the item isn't created typeless; change the push
payload in CreateResourceSyncsForm (the arrayHelpers.push call) to set type:
allowedRSTypes?.[0] ?? defaultResourceSyncType (or a sensible constant you
define), and add a small defaultResourceSyncType constant near the component so
when allowedRSTypes is undefined the new item still has a valid type; this
touches ResourceSyncFormValue, the arrayHelpers.push call, and uses
allowedRSTypes and useEffect for consistency.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 649220f1-2771-455e-8912-06e64337644c

📥 Commits

Reviewing files that changed from the base of the PR and between 61832d3 and 9e64b7d.

📒 Files selected for processing (13)
  • libs/i18n/locales/en/translation.json
  • libs/ui-components/src/components/Catalog/CatalogLandingPage.css
  • libs/ui-components/src/components/Catalog/CatalogLandingPage.tsx
  • libs/ui-components/src/components/Catalog/CatalogPage.tsx
  • libs/ui-components/src/components/Catalog/CatalogPageToolbar.tsx
  • libs/ui-components/src/components/Catalog/ResourceCatalog/ResourceCatalogPage.tsx
  • libs/ui-components/src/components/Repository/CreateRepository/CreateRepository.tsx
  • libs/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx
  • libs/ui-components/src/components/Repository/CreateRepository/CreateResourceSyncsForm.tsx
  • libs/ui-components/src/components/Repository/CreateRepository/utils.ts
  • libs/ui-components/src/hooks/useAppLinks.ts
  • libs/ui-components/src/hooks/useNavigate.tsx
  • libs/ui-components/src/types/rbac.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • libs/ui-components/src/components/Catalog/ResourceCatalog/ResourceCatalogPage.tsx
  • libs/ui-components/src/types/rbac.ts
  • libs/ui-components/src/components/Catalog/CatalogLandingPage.css
  • libs/ui-components/src/components/Catalog/CatalogPageToolbar.tsx
  • libs/ui-components/src/components/Repository/CreateRepository/utils.ts
  • libs/ui-components/src/hooks/useNavigate.tsx

Copy link
Collaborator

@celdrake celdrake left a comment

Choose a reason for hiding this comment

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

Mostly comments regarding UX, and some copy changes which IMO are necessary.

</StackItem>
<StackItem>
<Card isExpanded={!!expandedItem}>
<CardSection
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should the first section be expanded by default?

<GridItem>
<Stack hasGutter>
<StackItem>
<Content component="h2">{t('Available now')}</Content>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Adding the "Available now" block but not the "Coming soon" blocks seems odd to me, is that what UX expected?

<Divider />
<CardSection
title={t('Learn more')}
icon={<BookOpenIcon />}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Icon is not the one I see in the design.

>
<Stack hasGutter>
<StackItem>
{t('Explore documentation and resources to get the most out of the Software Catalog.')}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Content is not the same I see in the design.

</ListItem>
</List>
</StackItem>
<StackItem>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we even show this block if the user can't create image builds?
And what about viewer users that wouldn't have any "createX" permission?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

IMO yes
at least they know about the possibility and could ask somebody with permissions ?

Copy link
Collaborator

Choose a reason for hiding this comment

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

IDK, perhaps the copy should be adapted in those cases?
But it's for UX to decide, feel free to dismiss if not applicable.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
libs/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx (1)

434-440: ⚠️ Potential issue | 🟠 Major

Forced checkbox state and body visibility are not aligned.

At Line 434-Line 440, isChecked can be forced true by mustUseResourceSyncs, but the body still depends only on values.useResourceSyncs. This can hide the resource-sync form while the checkbox appears enabled/checked-by-policy.

🔧 Suggested fix
-              isChecked={options?.mustUseResourceSyncs ? true : values.useResourceSyncs}
-              isDisabled={!!options?.mustUseResourceSyncs}
+              isChecked={!!options?.mustUseResourceSyncs || values.useResourceSyncs}
+              isDisabled={!!options?.mustUseResourceSyncs}
               onChange={(_, checked) => {
                 // Trigger validation of the resource syncs items
                 return setFieldValue('useResourceSyncs', checked, true);
               }}
-              body={values.useResourceSyncs && <CreateResourceSyncsForm allowedRSTypes={options?.allowedRSTypes} />}
+              body={
+                (!!options?.mustUseResourceSyncs || values.useResourceSyncs) && (
+                  <CreateResourceSyncsForm allowedRSTypes={options?.allowedRSTypes} />
+                )
+              }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@libs/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx`
around lines 434 - 440, The checkbox visual state can be forced by
options?.mustUseResourceSyncs while the body rendering still uses
values.useResourceSyncs, causing the resource-sync form to be hidden when the
checkbox appears checked by policy; update the rendering and handler in
CreateRepositoryForm so body visibility uses the effective checked state (e.g.,
const effectiveUseResourceSyncs = options?.mustUseResourceSyncs ? true :
values.useResourceSyncs) and render CreateResourceSyncsForm when
effectiveUseResourceSyncs is true, and ensure onChange only calls
setFieldValue('useResourceSyncs', ...) when the control is not forced (i.e.,
when !options?.mustUseResourceSyncs) to avoid updating values incorrectly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@libs/ui-components/src/components/Repository/CreateRepository/CreateRepository.tsx`:
- Around line 39-40: The query-param boolean parsing for isCatalogSync is too
permissive: change the check that sets isCatalogSync (currently using
!!searchParams.get('isCatalogSync')) to a strict boolean parse (e.g., compare
the raw value from searchParams.get('isCatalogSync') against the string "true"
or explicitly accept only allowed truthy tokens) so that values like "false" do
not evaluate to true; update the logic in CreateRepository (where searchParams
and isCatalogSync are used) accordingly to rely on the strict comparison.

---

Duplicate comments:
In
`@libs/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx`:
- Around line 434-440: The checkbox visual state can be forced by
options?.mustUseResourceSyncs while the body rendering still uses
values.useResourceSyncs, causing the resource-sync form to be hidden when the
checkbox appears checked by policy; update the rendering and handler in
CreateRepositoryForm so body visibility uses the effective checked state (e.g.,
const effectiveUseResourceSyncs = options?.mustUseResourceSyncs ? true :
values.useResourceSyncs) and render CreateResourceSyncsForm when
effectiveUseResourceSyncs is true, and ensure onChange only calls
setFieldValue('useResourceSyncs', ...) when the control is not forced (i.e.,
when !options?.mustUseResourceSyncs) to avoid updating values incorrectly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4a2ed4d9-573a-4e46-9077-9b84e3929745

📥 Commits

Reviewing files that changed from the base of the PR and between 9e64b7d and 6cb508a.

📒 Files selected for processing (13)
  • libs/i18n/locales/en/translation.json
  • libs/ui-components/src/components/Catalog/CatalogLandingPage.css
  • libs/ui-components/src/components/Catalog/CatalogLandingPage.tsx
  • libs/ui-components/src/components/Catalog/CatalogPage.tsx
  • libs/ui-components/src/components/Catalog/CatalogPageToolbar.tsx
  • libs/ui-components/src/components/Catalog/ResourceCatalog/ResourceCatalogPage.tsx
  • libs/ui-components/src/components/Repository/CreateRepository/CreateRepository.tsx
  • libs/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx
  • libs/ui-components/src/components/Repository/CreateRepository/CreateResourceSyncsForm.tsx
  • libs/ui-components/src/components/Repository/CreateRepository/utils.ts
  • libs/ui-components/src/hooks/useAppLinks.ts
  • libs/ui-components/src/hooks/useNavigate.tsx
  • libs/ui-components/src/types/rbac.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • libs/ui-components/src/types/rbac.ts
  • libs/ui-components/src/components/Catalog/ResourceCatalog/ResourceCatalogPage.tsx
  • libs/ui-components/src/hooks/useAppLinks.ts
  • libs/ui-components/src/hooks/useNavigate.tsx
  • libs/ui-components/src/components/Catalog/CatalogLandingPage.css

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
libs/ui-components/src/components/Repository/CreateRepository/CreateResourceSyncsForm.tsx (1)

124-124: ⚠️ Potential issue | 🟡 Minor

Missing type field when adding a new resource sync.

The pushed object is missing the type field, which is required by ResourceSyncFormValue. While the useEffect at lines 28-32 will correct this at runtime, the object doesn't match the TypeScript type being cast.

🔧 Suggested fix
-            onClick={() => arrayHelpers.push({ name: '', path: '', targetRevision: '' } as ResourceSyncFormValue)}
+            onClick={() => arrayHelpers.push({ name: '', path: '', targetRevision: '', type: allowedRSTypes?.[0] ?? ResourceSyncType.ResourceSyncTypeFleet } as ResourceSyncFormValue)}

This also requires passing allowedRSTypes to the callback scope or importing ResourceSyncType if not already imported in the relevant scope.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@libs/ui-components/src/components/Repository/CreateRepository/CreateResourceSyncsForm.tsx`
at line 124, The object pushed in the onClick handler for arrayHelpers.push does
not include the required `type` field of `ResourceSyncFormValue`; update the
pushed object to include a valid `type` (e.g., default to the first entry in
`allowedRSTypes` or a specific `ResourceSyncType`) so it conforms to the
`ResourceSyncFormValue` type instead of relying on the useEffect to fix it
later, and ensure `allowedRSTypes` or `ResourceSyncType` is available in the
handler scope (pass `allowedRSTypes` into the callback or import
`ResourceSyncType`).
🧹 Nitpick comments (1)
libs/ui-components/src/components/Repository/CreateRepository/CreateResourceSyncsForm.tsx (1)

28-32: setValue in dependency array may cause re-renders.

The setValue function from Formik's useField is not guaranteed to be referentially stable across renders, which could trigger unnecessary effect executions. Consider wrapping the effect body or removing setValue from dependencies if the behavior is intended to run only when allowedRSTypes or value change.

♻️ Suggested stabilization
   React.useEffect(() => {
     if (allowedRSTypes?.length && !allowedRSTypes.includes(value)) {
       setValue(allowedRSTypes[0]);
     }
-  }, [allowedRSTypes, value, setValue]);
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [allowedRSTypes, value]);

Alternatively, use Formik's setFieldValue from useFormikContext which has stable identity.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@libs/ui-components/src/components/Repository/CreateRepository/CreateResourceSyncsForm.tsx`
around lines 28 - 32, The effect in CreateResourceSyncsForm uses setValue from
useField in its dependency array which may be unstable and cause extra runs;
change the effect to depend only on allowedRSTypes and value and call a stable
setter instead—either obtain setFieldValue from useFormikContext (stable
identity) and call setFieldValue(fieldName, allowedRSTypes[0]) when
allowedRSTypes?.length && !allowedRSTypes.includes(value), or keep setValue out
of deps by wrapping the effect body in a callback that references setValue via a
ref; update references to allowedRSTypes, value and the field name used by
useField so the effect triggers only when allowedRSTypes or value change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@libs/ui-components/src/components/Catalog/CatalogLandingPage.tsx`:
- Line 54: The CardHeader element in CatalogLandingPage uses role="button" but
lacks keyboard handlers and focusability; add tabIndex={0} and an onKeyDown
handler that listens for Enter and Space (keys 'Enter' and ' ' / keyCode 13 and
32) to invoke the existing onExpand handler (calling onExpand() and preventing
default for Space to avoid page scroll). Also consider setting aria-expanded
based on the component's expanded state so assistive tech reflects the toggle
state.

In `@libs/ui-components/src/components/Catalog/CatalogPage.tsx`:
- Around line 267-269: The code is using empty-string fallbacks (e.g., item.id
|| '' and metadata.name || '') which allows invalid empty IDs into catalogFilter
state; remove the "|| ''" fallbacks and treat resource.metadata.name and item.id
as defined by the API, updating calls to catalogFilter.catalogs.includes and
catalogFilter.setCatalogs to use item.id (or metadata.name) directly so no empty
string can be inserted—specifically update the branches that call
catalogFilter.catalogs.includes(item.id || '') and
catalogFilter.setCatalogs((catalogs) => catalogs.filter((c) => c !== item.id ||
'')) / setCatalogs((catalogs) => [...catalogs, item.id || '']) and the other
occurrences that use metadata.name || '' to use metadata.name directly.

---

Outside diff comments:
In
`@libs/ui-components/src/components/Repository/CreateRepository/CreateResourceSyncsForm.tsx`:
- Line 124: The object pushed in the onClick handler for arrayHelpers.push does
not include the required `type` field of `ResourceSyncFormValue`; update the
pushed object to include a valid `type` (e.g., default to the first entry in
`allowedRSTypes` or a specific `ResourceSyncType`) so it conforms to the
`ResourceSyncFormValue` type instead of relying on the useEffect to fix it
later, and ensure `allowedRSTypes` or `ResourceSyncType` is available in the
handler scope (pass `allowedRSTypes` into the callback or import
`ResourceSyncType`).

---

Nitpick comments:
In
`@libs/ui-components/src/components/Repository/CreateRepository/CreateResourceSyncsForm.tsx`:
- Around line 28-32: The effect in CreateResourceSyncsForm uses setValue from
useField in its dependency array which may be unstable and cause extra runs;
change the effect to depend only on allowedRSTypes and value and call a stable
setter instead—either obtain setFieldValue from useFormikContext (stable
identity) and call setFieldValue(fieldName, allowedRSTypes[0]) when
allowedRSTypes?.length && !allowedRSTypes.includes(value), or keep setValue out
of deps by wrapping the effect body in a callback that references setValue via a
ref; update references to allowedRSTypes, value and the field name used by
useField so the effect triggers only when allowedRSTypes or value change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7d4386ac-12f3-415b-a89d-5e217db9c5ba

📥 Commits

Reviewing files that changed from the base of the PR and between 6cb508a and ba59b1a.

📒 Files selected for processing (13)
  • libs/i18n/locales/en/translation.json
  • libs/ui-components/src/components/Catalog/CatalogLandingPage.css
  • libs/ui-components/src/components/Catalog/CatalogLandingPage.tsx
  • libs/ui-components/src/components/Catalog/CatalogPage.tsx
  • libs/ui-components/src/components/Catalog/CatalogPageToolbar.tsx
  • libs/ui-components/src/components/Catalog/ResourceCatalog/ResourceCatalogPage.tsx
  • libs/ui-components/src/components/Repository/CreateRepository/CreateRepository.tsx
  • libs/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx
  • libs/ui-components/src/components/Repository/CreateRepository/CreateResourceSyncsForm.tsx
  • libs/ui-components/src/components/Repository/CreateRepository/utils.ts
  • libs/ui-components/src/hooks/useAppLinks.ts
  • libs/ui-components/src/hooks/useNavigate.tsx
  • libs/ui-components/src/types/rbac.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • libs/ui-components/src/components/Catalog/CatalogPageToolbar.tsx
  • libs/ui-components/src/components/Catalog/CatalogLandingPage.css
  • libs/ui-components/src/components/Catalog/ResourceCatalog/ResourceCatalogPage.tsx
  • libs/ui-components/src/hooks/useNavigate.tsx

}>) => {
return (
<>
<CardHeader onClick={onExpand} role="button" className="fctl-catalog-landing__header">
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add keyboard support for the expandable section header.

Line 54 sets role="button" but only wires onClick; keyboard users can’t toggle with Enter/Space.

♿ Suggested fix
-      <CardHeader onClick={onExpand} role="button" className="fctl-catalog-landing__header">
+      <CardHeader
+        onClick={onExpand}
+        onKeyDown={(e) => {
+          if (e.key === 'Enter' || e.key === ' ') {
+            e.preventDefault();
+            onExpand();
+          }
+        }}
+        role="button"
+        tabIndex={0}
+        aria-expanded={isExpanded}
+        className="fctl-catalog-landing__header"
+      >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/ui-components/src/components/Catalog/CatalogLandingPage.tsx` at line 54,
The CardHeader element in CatalogLandingPage uses role="button" but lacks
keyboard handlers and focusability; add tabIndex={0} and an onKeyDown handler
that listens for Enter and Space (keys 'Enter' and ' ' / keyCode 13 and 32) to
invoke the existing onExpand handler (calling onExpand() and preventing default
for Space to avoid page scroll). Also consider setting aria-expanded based on
the component's expanded state so assistive tech reflects the toggle state.

Comment on lines +267 to +269
catalogFilter.catalogs.includes(item.id || '')
? catalogFilter.setCatalogs((catalogs) => catalogs.filter((c) => c !== item.id || ''))
: catalogFilter.setCatalogs((catalogs) => [...catalogs, item.id || '']);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Avoid empty-string fallbacks for API metadata.name in catalog state paths.

Lines 275-279 and Line 357 use metadata.name || '', and Lines 267-269 do the same with item.id || ''. This weakens the API invariant and can pollute filter state with empty IDs.

🛠️ Suggested fix
-                        onCheck={(_, item) => {
-                          catalogFilter.catalogs.includes(item.id || '')
-                            ? catalogFilter.setCatalogs((catalogs) => catalogs.filter((c) => c !== item.id || ''))
-                            : catalogFilter.setCatalogs((catalogs) => [...catalogs, item.id || '']);
-                        }}
+                        onCheck={(_, item) => {
+                          const catalogName = item.id as string;
+                          catalogFilter.setCatalogs((catalogs) =>
+                            catalogs.includes(catalogName)
+                              ? catalogs.filter((c) => c !== catalogName)
+                              : [...catalogs, catalogName],
+                          );
+                        }}
@@
-                            name: c.spec.displayName || c.metadata.name || '',
-                            id: c.metadata.name || '',
+                            name: c.spec.displayName || c.metadata.name,
+                            id: c.metadata.name,
                             checkProps: {
-                              checked: !!c.metadata.name && catalogFilter.catalogs.includes(c.metadata.name),
+                              checked: catalogFilter.catalogs.includes(c.metadata.name),
                             },
@@
-                                itemName: ci.metadata.name || '',
+                                itemName: ci.metadata.name,

As per coding guidelines: "Treat resource.metadata.name as always defined for resources returned from the API or created resources, and avoid optional chaining in these contexts; only handle undefined for partial or input payloads".

Also applies to: 275-279, 357-358

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/ui-components/src/components/Catalog/CatalogPage.tsx` around lines 267 -
269, The code is using empty-string fallbacks (e.g., item.id || '' and
metadata.name || '') which allows invalid empty IDs into catalogFilter state;
remove the "|| ''" fallbacks and treat resource.metadata.name and item.id as
defined by the API, updating calls to catalogFilter.catalogs.includes and
catalogFilter.setCatalogs to use item.id (or metadata.name) directly so no empty
string can be inserted—specifically update the branches that call
catalogFilter.catalogs.includes(item.id || '') and
catalogFilter.setCatalogs((catalogs) => catalogs.filter((c) => c !== item.id ||
'')) / setCatalogs((catalogs) => [...catalogs, item.id || '']) and the other
occurrences that use metadata.name || '' to use metadata.name directly.

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.

2 participants