Skip to content

Conversation

@ibolton336
Copy link
Member

@ibolton336 ibolton336 commented Jan 28, 2026

Screen.Recording.2026-01-28.at.5.23.04.PM.mov

Improve Profile Manager UI/UX

Summary

Redesigns the Profile Manager screen with improved layout, visual hierarchy, and user experience. The changes address multiple UX issues including the sparse empty state, cramped form layout, and various styling inconsistencies.

Changes

Layout & Structure

  • Fixed split-panel layout: Left profile list remains fixed while right panel scrolls independently using custom flexbox layout
  • Added masthead header: Contains "New Profile" split button dropdown and displays selected profile info (name, active/read-only badges, auto-save status)
  • Improved empty state: Replaced minimal empty state with proper PatternFly EmptyState component featuring icon, descriptive text, and action buttons

Profile List (Left Panel)

  • Switched from DataList to SimpleList for cleaner, less bordered appearance
  • Replaced verbose "active" label with compact star icon (★) to save horizontal space
  • Fixed profile name truncation to allow more characters before ellipsis
  • Fixed kebab menu wrapping to second line when profile is active
  • Added appendTo: "inline" to dropdown for proper positioning

Profile Editor Form (Right Panel)

  • Removed width limitation - form now uses full available width
  • Moved profile name, status badges, and auto-save indicator to masthead
  • Replaced large alert banners with compact HelperText items for validation errors and disabled warnings
  • Changed custom rules labels from filled blue to outline variant for correct icon colors in VS Code theme

Styling Fixes

  • Fixed split button border color appearing light grey instead of dark
  • Added CSS override for .pf-v6-c-menu-toggle.pf-m-split-button.pf-m-primary border color

Files Changed

  • webview-ui/src/components/ProfileManager/ProfileManagerPage.tsx
  • webview-ui/src/components/ProfileManager/ProfileList.tsx
  • webview-ui/src/components/ProfileManager/ProfileEditorForm.tsx
  • webview-ui/src/index.css

Summary by CodeRabbit

  • UI Improvements

    • Redesigned profile list with inline icons, per-item action dropdowns, updated truncation/spacing, and centralized delete confirmation.
    • New masthead and two-pane layout with improved empty-state, creation/import actions, and fixed sidebar/editor behavior.
    • Consolidated status region showing validation or disabled state; streamlined action area and updated tag/button styling (outlined tags, smaller secondary/link actions).
  • Tests

    • Updated end-to-end selectors/text for "New Profile" and "Delete" label changes.

✏️ Tip: You can customize this high-level summary in your review settings.

@snyk-io
Copy link

snyk-io bot commented Jan 28, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@ibolton336 ibolton336 changed the title ✨ Profiles form UX improvement ✨ Manage Profiles page UX improvements Jan 28, 2026
@ibolton336 ibolton336 force-pushed the profiles-form-empty-state branch from b98d364 to 88be395 Compare January 28, 2026 22:32
@ibolton336 ibolton336 requested a review from a team as a code owner January 28, 2026 22:32
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 28, 2026

📝 Walkthrough

Walkthrough

Refactors the Profile Manager UI across multiple files. ProfileEditorForm: consolidates status UI into a HelperText region, removes isPending from a debounce hook return, updates validation handling, changes rule tag styling to outlined, and simplifies active/delete controls and layout/spacing. ProfileList: replaces DataList with SimpleList, adds per-item lock/star indicators, moves actions into per-item dropdown, trims the creation API by removing onCreate, and centralizes delete confirmation. ProfileManagerPage: replaces split layout with fixed left list + scrollable editor, adds a masthead with dropdown and empty-state flows. index.css and E2E tests updated for UI label and split-button styling.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: UX improvements to the Profile Manager page. It is concise, specific, and directly reflects the primary focus of the changeset.
Description check ✅ Passed The description is well-structured with clear sections covering Summary, Changes (with subsections), and Files Changed. It provides sufficient detail about layout, UI components, and styling updates without being overly verbose.
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 docstrings

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.

Copy link
Contributor

@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

🤖 Fix all issues with AI agents
In `@webview-ui/src/components/ProfileManager/ProfileList.tsx`:
- Around line 128-138: The Delete DropdownItem currently only disables based on
profile.readOnly; update its isDisabled prop to also respect the component’s
isDisabled analysis flag (same as Make Active and Duplicate) so deletion is
blocked during analysis — locate the DropdownItem that calls
setProfileToDelete(profile) and setIsOpen(false) and change its isDisabled check
to combine profile.readOnly with the existing isDisabled flag (e.g., isDisabled
|| profile.readOnly) so it mirrors the other actions.

In `@webview-ui/src/components/ProfileManager/ProfileManagerPage.tsx`:
- Around line 246-248: The JSX block rendering ProfileEditorForm has extra
two-space indentation causing lint errors; locate the conditional rendering
around selectedProfile in ProfileManagerPage (the ternary/conditional that
returns <ProfileEditorForm ... />) and remove the extra leading spaces so the
opening tag for ProfileEditorForm and its props (e.g., allProfiles={profiles})
align with the surrounding JSX indentation style used in ProfileManagerPage.tsx;
ensure the closing tag and any nested props follow the same indentation to
satisfy the linter.
🧹 Nitpick comments (3)
webview-ui/src/index.css (1)

238-244: Consider using a CSS variable for theme consistency.

The hardcoded rgba(0, 0, 0, 0.25) may not work well across all VS Code themes (particularly light themes where a darker border might be more appropriate). Given that the rest of this file uses CSS variable mappings for theming, consider defining a variable or using an existing border color variable.

That said, if this value was tested across both light and dark VS Code themes and works acceptably, this is fine to keep as-is for now.

webview-ui/src/components/ProfileManager/ProfileManagerPage.tsx (1)

222-222: Magic number for masthead height.

The calc(100vh - 56px) assumes the masthead is exactly 56px tall. If PatternFly's masthead height changes in future versions, this could cause layout issues.

Consider either:

  • Adding a comment explaining the 56px assumption
  • Using a CSS variable if PatternFly provides one for masthead height

This is low-risk for now but worth noting.

webview-ui/src/components/ProfileManager/ProfileEditorForm.tsx (1)

38-55: Unused isPending state in useDebouncedCallback.

The hook still tracks isPending (lines 40, 44, 50, 53), but since line 81 only destructures callback, this state is computed but never read. This is dead code that could be cleaned up.

♻️ Simplified hook without isPending
 function useDebouncedCallback(callback: (...args: any[]) => void, delay: number) {
   const timeoutRef = useRef<NodeJS.Timeout | null>(null);
-  const [isPending, setIsPending] = useState(false);

-  return {
-    callback: (...args: any[]) => {
-      setIsPending(true);
-      if (timeoutRef.current) {
-        clearTimeout(timeoutRef.current);
-      }
-      timeoutRef.current = setTimeout(() => {
-        callback(...args);
-        setIsPending(false);
-      }, delay);
-    },
-    isPending,
-  };
+  return (...args: any[]) => {
+    if (timeoutRef.current) {
+      clearTimeout(timeoutRef.current);
+    }
+    timeoutRef.current = setTimeout(() => {
+      callback(...args);
+    }, delay);
+  };
 }

Then update usage:

-  const { callback: debouncedChange } = useDebouncedCallback(onChange, 300);
+  const debouncedChange = useDebouncedCallback(onChange, 300);

Comment on lines +128 to +138
<DropdownItem
key="delete"
onClick={() => {
setProfileToDelete(profile);
setIsOpen(false);
}}
isDisabled={profile.readOnly}
isDanger
>
Delete
</DropdownItem>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n webview-ui/src/components/ProfileManager/ProfileList.tsx | sed -n '100,160p'

Repository: konveyor/editor-extensions

Length of output: 2635


🏁 Script executed:

cat -n webview-ui/src/components/ProfileManager/ProfileList.tsx | head -50

Repository: konveyor/editor-extensions

Length of output: 2000


🏁 Script executed:

rg -n "ProfileList" webview-ui/src/components --type=ts -A 10 | head -80

Repository: konveyor/editor-extensions

Length of output: 3532


🏁 Script executed:

rg -n "handleDeleteProfile" webview-ui/src/components/ProfileManager --type=ts -B 2 -A 15

Repository: konveyor/editor-extensions

Length of output: 5159


Delete action should also respect isDisabled prop during analysis.

The Delete button only checks profile.readOnly (line 134) while Make Active (line 113) and Duplicate (line 124) respect the isDisabled prop (which reflects analysis state). This creates an inconsistency—users cannot modify profiles during analysis via Make Active or Duplicate, but can still initiate deletion.

For consistency and to prevent unintended profile deletion during active analysis, add the isDisabled check:

Suggested fix
                      <DropdownItem
                        key="delete"
                        onClick={() => {
                          setProfileToDelete(profile);
                          setIsOpen(false);
                        }}
-                        isDisabled={profile.readOnly}
+                        isDisabled={profile.readOnly || isDisabled}
                        isDanger
                      >
                        Delete
                      </DropdownItem>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<DropdownItem
key="delete"
onClick={() => {
setProfileToDelete(profile);
setIsOpen(false);
}}
isDisabled={profile.readOnly}
isDanger
>
Delete
</DropdownItem>
<DropdownItem
key="delete"
onClick={() => {
setProfileToDelete(profile);
setIsOpen(false);
}}
isDisabled={profile.readOnly || isDisabled}
isDanger
>
Delete
</DropdownItem>
🤖 Prompt for AI Agents
In `@webview-ui/src/components/ProfileManager/ProfileList.tsx` around lines 128 -
138, The Delete DropdownItem currently only disables based on profile.readOnly;
update its isDisabled prop to also respect the component’s isDisabled analysis
flag (same as Make Active and Duplicate) so deletion is blocked during analysis
— locate the DropdownItem that calls setProfileToDelete(profile) and
setIsOpen(false) and change its isDisabled check to combine profile.readOnly
with the existing isDisabled flag (e.g., isDisabled || profile.readOnly) so it
mirrors the other actions.

@ibolton336 ibolton336 requested a review from a team as a code owner January 29, 2026 14:03
@ibolton336 ibolton336 force-pushed the profiles-form-empty-state branch from a664358 to ddfb8c0 Compare January 29, 2026 14:27
- Add masthead with split button for New Profile/Import actions
- Fixed split-panel layout with independent scrolling
- Replace empty state with proper PatternFly EmptyState component
- Switch profile list from DataList to SimpleList
- Replace active label with compact star icon
- Fix profile name truncation and kebab menu wrapping
- Move profile info and auto-save status to masthead
- Fix split button border color in theme
- Change custom rules labels to outline variant

Signed-off-by: Ian Bolton <[email protected]>
Signed-off-by: Ian Bolton <[email protected]>
Signed-off-by: Ian Bolton <[email protected]>
@ibolton336 ibolton336 force-pushed the profiles-form-empty-state branch from 7b8a2d3 to 13383c0 Compare January 29, 2026 16:12
Copy link
Contributor

@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

🤖 Fix all issues with AI agents
In `@webview-ui/src/components/ProfileManager/ProfileList.tsx`:
- Around line 147-159: The ConfirmDialog usage in ProfileList passes
confirmButtonText="Delete" but ConfirmDialog component does not destructure or
forward confirmButtonText to WarningModal; update the ConfirmDialog component to
destructure confirmButtonText (alongside confirmButtonVariant) and pass it into
the underlying WarningModal (the same place confirmButtonVariant is forwarded),
so the "Delete" label from ProfileList is actually used; check ConfirmDialog's
parameter list and its call to WarningModal to add this prop.
🧹 Nitpick comments (3)
webview-ui/src/components/ProfileManager/ProfileManagerPage.tsx (2)

201-213: Auto-saved indicator is static.

The "Auto-saved" status always displays regardless of actual save state. The useDebouncedCallback hook in ProfileEditorForm.tsx exposes isPending which could indicate when a save is in progress. Consider lifting this state up or using a context to show "Saving..." during debounce and "Auto-saved" after completion for better user feedback.


222-222: Consider using CSS variable or calc for masthead height.

The hardcoded 56px for masthead height is fragile. If the masthead styling changes, this will need manual adjustment. Consider using a CSS variable or measuring the actual masthead height dynamically.

webview-ui/src/components/ProfileManager/ProfileEditorForm.tsx (1)

38-55: isPending is defined but never used.

The useDebouncedCallback hook (lines 40, 53) exposes isPending state, but line 81 only destructures callback. This creates unused state updates. Either use isPending to show a saving indicator (which would address the static "Auto-saved" feedback in the masthead), or remove it from the hook to avoid unnecessary re-renders.

Option A: Remove unused state from hook
 function useDebouncedCallback(callback: (...args: any[]) => void, delay: number) {
   const timeoutRef = useRef<NodeJS.Timeout | null>(null);
-  const [isPending, setIsPending] = useState(false);

-  return {
-    callback: (...args: any[]) => {
-      setIsPending(true);
+  return (...args: any[]) => {
       if (timeoutRef.current) {
         clearTimeout(timeoutRef.current);
       }
       timeoutRef.current = setTimeout(() => {
         callback(...args);
-        setIsPending(false);
       }, delay);
-    },
-    isPending,
   };
 }

Then update line 81:

-  const { callback: debouncedChange } = useDebouncedCallback(onChange, 300);
+  const debouncedChange = useDebouncedCallback(onChange, 300);

Also applies to: 81-81

Comment on lines +147 to +159
<ConfirmDialog
isOpen={profileToDelete !== null}
title="Delete profile?"
message={`Are you sure you want to delete the profile "${profileToDelete?.name}"? This action cannot be undone.`}
confirmButtonText="Delete"
onConfirm={() => {
if (profileToDelete) {
onDelete(profileToDelete.id);
}
setProfileToDelete(null);
}}
onCancel={() => setProfileToDelete(null)}
/>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, locate the ConfirmDialog component
fd -type f -name "*ConfirmDialog*" webview-ui/src/components

# Then search for confirmButtonText usage
rg -n "confirmButtonText" webview-ui/src/components --type tsx --type ts

Repository: konveyor/editor-extensions

Length of output: 268


🏁 Script executed:

# Locate the ConfirmDialog component
fd "ConfirmDialog" webview-ui/src/components

# Search for confirmButtonText usage
rg "confirmButtonText" webview-ui/src/components -n

Repository: konveyor/editor-extensions

Length of output: 457


🏁 Script executed:

cat -n webview-ui/src/components/ConfirmDialog/ConfirmDialog.tsx

Repository: konveyor/editor-extensions

Length of output: 1071


confirmButtonText prop is not used by ConfirmDialog.

The confirmButtonText="Delete" prop passed here has no effect. Although defined in the ConfirmDialog interface, it is not destructured in the component parameters and is never passed to the underlying WarningModal component. Compare with confirmButtonVariant, which is properly destructured (line 19) and passed to WarningModal (line 27).

🤖 Prompt for AI Agents
In `@webview-ui/src/components/ProfileManager/ProfileList.tsx` around lines 147 -
159, The ConfirmDialog usage in ProfileList passes confirmButtonText="Delete"
but ConfirmDialog component does not destructure or forward confirmButtonText to
WarningModal; update the ConfirmDialog component to destructure
confirmButtonText (alongside confirmButtonVariant) and pass it into the
underlying WarningModal (the same place confirmButtonVariant is forwarded), so
the "Delete" label from ProfileList is actually used; check ConfirmDialog's
parameter list and its call to WarningModal to add this prop.

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.

1 participant