Standardize profiles#508
Conversation
There was a problem hiding this comment.
Pull request overview
This PR consolidates multiple “profile” feature UIs into a standardized shared profiles feature, replacing several feature-specific headers/lists/sidepanels and aligning profile types around a shared Profile base type.
Changes:
- Introduces a new
src/features/profilesfeature (container/header/list/actions, shared side panel, shared remove/archive modal). - Refactors multiple profile feature type definitions to extend the shared
Profileinterface. - Adds new “view details blocks/tabs” for profile-specific details (e.g., WSL/upgrade/security/script/repository) and removes older feature-specific components/tests.
Reviewed changes
Copilot reviewed 286 out of 343 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| src/features/wsl-profiles/components/WslProfileNonCompliantInstancesList/WslProfileNonCompliantInstancesList.tsx | Fixes typing/import path and expands memo deps to match new handlers/state. |
| src/features/wsl-profiles/api/useRemoveWslProfile.ts | Renames delete hook/params to standardized “remove” naming. |
| src/features/wsl-profiles/api/index.ts | Updates exports to the renamed remove hook. |
| src/features/wsl-profiles/components/ViewWslProfileDetailsBlock/ViewWslProfileDetailsBlock.tsx | Adds standardized “view details” block for WSL profiles. |
| src/features/upgrade-profiles/types/index.ts | Switches to export type for type-only exports. |
| src/features/upgrade-profiles/types/UpgradeProfile.ts | Makes UpgradeProfile extend shared Profile. |
| src/features/upgrade-profiles/index.ts | Re-exports standardized view-details block and adjusts public API surface. |
| src/features/upgrade-profiles/components/ViewUpgradeProfileDetailsBlock/ViewUpgradeProfileDetailsBlock.tsx | Adds standardized “view details” block for upgrade profiles. |
| src/features/security-profiles/types/index.ts | Adds explicit type-only exports. |
| src/features/security-profiles/types/SecurityProfile.ts | Defines SecurityProfile as extending shared Profile. |
| src/features/security-profiles/index.ts | Re-exports new blocks/notifications/helpers and updates API exports. |
| src/features/security-profiles/helpers.tsx | Renames schedule helper and adds “short” schedule label + UTC formatting. |
| src/features/security-profiles/components/ViewSecurityProfileDetailsBlock/ViewSecurityProfileDetailsBlock.tsx | Adds standardized “view details” block for security profiles. |
| src/features/security-profiles/components/SecurityProfileLastRunWithSchedule/SecurityProfileLastRunWithSchedule.tsx | Adds last-run/schedule cell with tooltip + truncation styling. |
| src/features/security-profiles/api/useIsSecurityProfilesLimitReached.ts | Replaces self-import with relative constants import. |
| src/features/security-profiles/api/useGetPageSecurityProfile.tsx | Avoids barrel import for hook to reduce coupling. |
| src/features/script-profiles/types/ScriptProfile.ts | Makes ScriptProfile extend shared Profile. |
| src/features/script-profiles/index.ts | Re-exports standardized view-details + activity history + archive API hook. |
| src/features/script-profiles/components/ViewScriptProfileDetailsBlock/ViewScriptProfileDetailsBlock.tsx | Adds standardized “view details” block for script profiles. |
| src/features/script-profiles/api/useGetScriptProfileLimits.ts | Aligns React Query pending flag naming to isPending. |
| src/features/script-profiles/api/useGetPageScriptProfile.ts | Avoids barrel import for hook to reduce coupling. |
| src/features/repository-profiles/types/index.ts | Adds explicit type exports for repository profile types/form values. |
| src/features/repository-profiles/types/RepositoryProfile.ts | Makes RepositoryProfile extend shared Profile and exports pocket type. |
| src/features/repository-profiles/index.ts | Exposes new manage side panel + view tabs. |
| src/features/repository-profiles/helpers.ts | Adds pocket-to-distribution/series grouping helper. |
| src/features/repository-profiles/components/ViewRepositoryProfilePocketsTab/ViewRepositoryProfilePocketsTab.tsx | Adds standardized pockets viewing tab. |
| src/features/repository-profiles/components/ViewRepositoryProfileAptSourcesTab/ViewRepositoryProfileAptSourcesTab.tsx | Adds standardized APT sources viewing tab. |
| src/features/repository-profiles/components/RepositoryProfileManageSidePanel/RepositoryProfileManageSidePanel.tsx | Adds unified add/edit sidepanel wrapper. |
| src/features/repository-profiles/api/useGetRepositoryProfile.ts | Adds query hook to fetch a single repository profile by name. |
| src/features/repository-profiles/api/useGetPageRepositoryProfile.ts | Adds page-param driven repository-profile fetching hook. |
| src/features/repository-profiles/api/index.ts | Adds exports for new repository profile query hooks. |
| src/features/removal-profiles/types/index.ts | Adds explicit type export. |
| src/features/removal-profiles/types/RemovalProfile.ts | Adds shared Profile-based removal profile type. |
| src/features/removal-profiles/index.ts | Re-exports standardized view-details block and updates public API surface. |
| src/features/removal-profiles/components/ViewRemovalProfileDetailsBlock/ViewRemovalProfileDetailsBlock.tsx | Adds standardized “view details” block for removal profiles. |
| src/features/removal-profiles/api/useGetPageRemovalProfile.ts | Avoids barrel import for hook to reduce coupling. |
| src/features/reboot-profiles/types/RebootProfile.ts | Adds shared Profile-based reboot profile type. |
| src/features/reboot-profiles/index.ts | Updates public surface to expose key API hooks and type only. |
| src/features/reboot-profiles/api/useGetRebootProfiles.ts | Simplifies query hook shape and standardizes returned fields/names. |
| src/features/reboot-profiles/api/index.ts | Renames remove hook export to standardized name. |
| src/features/profiles/types/index.ts | Introduces shared Profile and related types. |
| src/features/profiles/types/Profiles.ts | Defines shared Profile interface + common helper types. |
| src/features/profiles/index.ts | Adds consolidated exports for the new shared profiles feature. |
| src/features/profiles/hooks/useOpenViewProfileSidePanel.tsx | Adds helper hook to open standardized view side panel via SidePanel context. |
| src/features/profiles/hooks/useOpenManageProfileSidePanel.ts | Adds helper hook to open add/edit/etc via page params consistently. |
| src/features/profiles/hooks/useGetProfileAssociatedCount.ts | Adds cross-profile logic for computing association counts. |
| src/features/profiles/hooks/useCloseManageProfileSidePanel.tsx | Adds helper hook to close sidepanel and clear params. |
| src/features/profiles/hooks/index.ts | Exports new profiles hooks. |
| src/features/profiles/components/ViewProfileSidePanel/ViewProfileSidePanel.tsx | Adds standardized view sidepanel with tabs + suspense blocks. |
| src/features/profiles/components/ViewProfileSidePanel/helpers.ts | Centralizes tab selection per profile type. |
| src/features/profiles/components/ViewProfileSidePanel/components/ViewProfileScheduleBlock/ViewProfileScheduleBlock.tsx | Adds standardized schedule/last-run/next-run block across profile types. |
| src/features/profiles/components/ViewProfileSidePanel/components/ViewProfileInfoTab/ViewProfileInfoTab.tsx | Composes standardized info tab blocks. |
| src/features/profiles/components/ViewProfileSidePanel/components/ViewProfileGeneralBlock/ViewProfileGeneralBlock.tsx | Adds standardized “General” info block. |
| src/features/profiles/components/ViewProfileSidePanel/components/ViewProfileDetailsBlock/ViewProfileDetailsBlock.tsx | Routes profile-specific details rendering via lazily loaded blocks. |
| src/features/profiles/components/ViewProfileSidePanel/components/ViewProfileAssociationBlock/ViewProfileAssociationBlock.tsx | Adds standardized association/compliance/tags block. |
| src/features/profiles/components/ViewProfileSidePanel/components/ViewProfileActionsBlock/ViewProfileActionsBlock.tsx | Adds standardized action buttons + remove/archive modal integration. |
| src/features/profiles/components/RemoveProfileModal/RemoveProfileModal.tsx | Adds shared remove/archive confirmation modal and wires it to useRemoveProfile. |
| src/features/profiles/components/RemoveProfileModal/helpers.ts | Centralizes modal + notification messages by profile type. |
| src/features/profiles/components/ProfilesContainer/ProfilesContainer.tsx | Adds shared container handling loading, empty, notifications, list, pagination. |
| src/features/profiles/components/ProfilesHeader/ProfilesHeader.tsx | Adds shared header with search + type-specific filters/actions. |
| src/features/profiles/components/ProfilesEmptyState/ProfilesEmptyState.tsx | Adds shared empty state with type-specific copy + docs links. |
| src/features/profiles/components/ProfilesEmptyState/helpers.ts | Centralizes empty-state messaging and help links by type. |
| src/features/profiles/components/ProfilesList/components/ProfilesListActions/ProfilesListActions.tsx | Adds shared list actions menu and modal handling. |
| src/features/profiles/components/ProfilesList/components/AssociatedInstancesCell/AssociatedInstancesCell.tsx | Adds shared “associated instances” cell logic. |
| src/features/profiles/components/ProfileAssociatedInstancesLink/ProfileAssociatedInstancesLink.tsx | Adds shared link/button behavior for associations, including WSL noncompliant sidepanel. |
| src/features/profiles/components/AddProfileButton/AddProfileButton.tsx | Adds shared “Add profile” CTA with WSL modal gating and limit disablement. |
| src/features/profiles/api/useRemoveProfile.ts | Adds shared remove/archive dispatcher across profile types using existing feature hooks. |
| src/context/profiles.tsx | Adds ProfilesContext and provider for tracking profile-limit state. |
| src/components/layout/InfoGrid/InfoGrid.tsx | Adds dense layout option to standardize tighter info-grid spacing. |
| src/components/layout/InfoGrid/InfoGrid.module.scss | Adds denseGrid styling. |
| src/components/layout/Chip/Chip.tsx | Adjusts chip spacing classes (removes right-margin utility). |
| src/components/layout/Blocks/Item/Item.tsx | Changes block heading styling to small caps. |
| src/components/layout/Blocks/Item/Item.module.scss | Tweaks spacing tokens and heading margin. |
| src/components/layout/Blocks/Blocks.module.scss | Adjusts blocks gap spacing. |
| src/features/wsl-profiles/components/WslProfilesList/WslProfilesList.test.tsx | Removes WSL profiles list tests as part of consolidation. |
| src/features/wsl-profiles/components/WslProfilesList/WslProfilesList.module.scss | Removes WSL profiles list styles as part of consolidation. |
| src/features/wsl-profiles/components/WslProfilesHeader/WslProfilesHeader.tsx | Removes WSL profiles header component (replaced by shared header). |
| src/features/wsl-profiles/components/WslProfilesHeader/WslProfilesHeader.test.tsx | Removes WSL profiles header tests. |
| src/features/wsl-profiles/components/WslProfilesEmptyState/WslProfilesEmptyState.tsx | Removes WSL empty state component (replaced by shared empty state). |
| src/features/wsl-profiles/components/WslProfilesEmptyState/WslProfilesEmptyState.test.tsx | Removes WSL empty state tests. |
| src/features/wsl-profiles/components/WslProfileRemoveModal/WslProfileRemoveModal.tsx | Removes WSL remove modal component (replaced by shared remove modal). |
| src/features/wsl-profiles/components/WslProfileNonCompliantParentsLink/WslProfileNonCompliantParentsLink.tsx | Removes WSL noncompliant parents link component (logic moved to shared association link). |
| src/features/wsl-profiles/components/WslProfileNonCompliantParentsLink/WslProfileNonCompliantParentsLink.test.tsx | Removes WSL noncompliant parents link tests. |
| src/features/wsl-profiles/components/WslProfileNonCompliantInstancesSidePanel/WslProfileNonCompliantInstancesSidePanel.tsx | Removes WSL noncompliant instances sidepanel component (replaced by shared sidepanel content usage). |
| src/features/wsl-profiles/components/WslProfileDetailsSidePanel/WslProfileDetailsSidePanel.test.tsx | Removes WSL profile details sidepanel tests. |
| src/features/wsl-profiles/components/WslProfileAddButton/constants.ts | Removes WSL add button localStorage constant file (inlined into shared add button). |
| src/features/upgrade-profiles/components/UpgradeProfilesHeader/UpgradeProfilesHeader.tsx | Removes upgrade profiles header component (replaced by shared header). |
| src/features/upgrade-profiles/components/UpgradeProfilesEmptyState/UpgradeProfilesEmptyState.tsx | Removes upgrade empty state component (replaced by shared empty state). |
| src/features/upgrade-profiles/components/UpgradeProfileRemoveModal/UpgradeProfileRemoveModal.tsx | Removes upgrade remove modal component (replaced by shared remove modal). |
| src/features/upgrade-profiles/components/UpgradeProfileListActions/UpgradeProfileListActions.tsx | Removes upgrade list actions component (replaced by shared list actions). |
| src/features/upgrade-profiles/components/UpgradeProfileList/UpgradeProfileList.test.tsx | Removes upgrade list tests. |
| src/features/upgrade-profiles/components/UpgradeProfileDetailsSidePanel/helpers.ts | Removes upgrade details schedule helper (now handled by shared schedule block). |
| src/features/upgrade-profiles/components/UpgradeProfileDetailsSidePanel/UpgradeProfileDetailsSidePanel.test.tsx | Removes upgrade details sidepanel tests. |
| src/features/security-profiles/components/SecurityProfilesList/SecurityProfilesList.test.tsx | Removes security profiles list tests. |
| src/features/security-profiles/components/SecurityProfilesHeader/SecurityProfilesHeader.tsx | Removes security profiles header component (replaced by shared header). |
| src/features/security-profiles/components/SecurityProfilesHeader/SecurityProfilesHeader.test.tsx | Removes security profiles header tests. |
| src/features/security-profiles/components/SecurityProfilesContainer/SecurityProfilesContainer.test.tsx | Removes security profiles container tests. |
| src/features/security-profiles/components/SecurityProfileArchiveModal/SecurityProfileArchiveModal.tsx | Removes security archive modal component (replaced by shared remove/archive modal). |
| src/features/security-profiles/components/SecurityProfileArchiveModal/SecurityProfileArchiveModal.test.tsx | Removes security archive modal tests. |
| src/features/script-profiles/components/ScriptProfilesPanel/ScriptProfilesPanel.tsx | Removes legacy script profiles panel (now consolidated in shared profiles flows). |
| src/features/script-profiles/components/ScriptProfilesPanel/ScriptProfilesPanel.test.tsx | Removes script profiles panel tests. |
| src/features/script-profiles/components/ScriptProfileInfo/ScriptProfileInfo.tsx | Removes legacy script profile info component (replaced by shared view blocks). |
| src/features/script-profiles/components/ScriptProfileInfo/ScriptProfileInfo.test.tsx | Removes script profile info tests. |
| src/features/script-profiles/components/ScriptProfileDetailsSidePanel/ScriptProfileDetailsSidePanel.tsx | Removes legacy script profile details sidepanel (replaced by shared view sidepanel). |
| src/features/script-profiles/components/ScriptProfileControl/ScriptProfileControl.tsx | Removes legacy script profile control block (replaced by shared actions block). |
| src/features/script-profiles/components/ScriptProfileControl/ScriptProfileControl.test.tsx | Removes script profile control tests. |
| src/features/script-profiles/components/ScriptProfileAssociatedInstancesLink/ScriptProfileAssociatedInstancesLink.tsx | Removes script associated instances link (replaced by shared association link). |
| src/features/script-profiles/components/ScriptProfileAssociatedInstancesLink/ScriptProfileAssociatedInstancesLink.test.tsx | Removes script associated instances link tests. |
| src/features/script-profiles/components/ScriptProfileArchiveModal/ScriptProfileArchiveModal.tsx | Removes script archive modal component (replaced by shared remove/archive modal). |
| src/features/script-profiles/components/ScriptProfileArchiveModal/ScriptProfileArchiveModal.test.tsx | Removes script archive modal tests. |
| src/features/script-profiles/components/NoScriptProfilesEmptyState/NoScriptProfilesEmptyState.tsx | Removes script empty state component (replaced by shared empty state). |
| src/features/script-profiles/components/NoScriptProfilesEmptyState/NoScriptProfilesEmptyState.test.tsx | Removes script empty state tests. |
| src/features/script-profiles/components/AddScriptProfileButton/AddScriptProfileButton.tsx | Removes script add button component (replaced by shared add button). |
| src/features/repository-profiles/components/RepositoryProfileList/RepositoryProfileList.tsx | Removes legacy repository profiles list implementation (replaced by consolidated profiles list). |
| src/features/repository-profiles/components/RepositoryProfileContainer/RepositoryProfileContainer.tsx | Removes legacy repository profiles container (replaced by shared container). |
| src/features/repository-profiles/components/RepositoryProfileHeader/RepositoryProfileHeader.tsx | Removes repository header (replaced by shared header). |
| src/features/repository-profiles/components/RepositoryProfileAddButton/RepositoryProfileAddButton.tsx | Removes repository add button (replaced by shared add button / manage sidepanel). |
| src/features/removal-profiles/components/RemovalProfilesHeader/RemovalProfilesHeader.tsx | Removes removal header (replaced by shared header). |
| src/features/removal-profiles/components/RemovalProfilesEmptyState/RemovalProfilesEmptyState.tsx | Removes removal empty state (replaced by shared empty state). |
| src/features/removal-profiles/components/RemovalProfileRemoveModal/RemovalProfileRemoveModal.tsx | Removes removal remove modal (replaced by shared remove modal). |
| src/features/removal-profiles/components/RemovalProfileListActions/RemovalProfileListActions.tsx | Removes removal list actions (replaced by shared list actions). |
| src/features/removal-profiles/components/RemovalProfileList/RemovalProfileList.test.tsx | Removes removal list tests. |
| src/features/removal-profiles/components/RemovalProfileDetailsSidePanel/RemovalProfileDetailsSidePanel.tsx | Removes removal details sidepanel (replaced by shared view sidepanel). |
| src/features/reboot-profiles/components/RebootProfilesContainer/RebootProfilesContainer.tsx | Removes reboot profiles container (replaced by shared container). |
| src/features/reboot-profiles/components/RebootProfilesHeader/RebootProfilesHeader.tsx | Removes reboot header (replaced by shared header). |
| src/features/reboot-profiles/components/RebootProfilesList/RebootProfilesList.test.tsx | Removes reboot list tests. |
| src/features/reboot-profiles/components/RebootProfileRemoveModal/RebootProfileRemoveModal.tsx | Removes reboot remove modal (replaced by shared remove modal). |
| src/features/reboot-profiles/components/RebootProfileDetailsSidePanel/helpers.ts | Removes reboot schedule formatting helper (now handled via shared schedule block). |
| src/features/reboot-profiles/components/RebootProfileDetailsSidePanel/RebootProfileDetailsSidePanel.test.tsx | Removes reboot details tests. |
| src/features/package-profiles/types/PackageProfile.ts | Makes PackageProfile extend shared Profile and extracts compliance counts type. |
| src/features/package-profiles/index.ts | Updates public surface and exports constraints details for shared side panel tabs. |
| src/features/package-profiles/components/PackageProfileDetailsConstraints/PackageProfileDetailsConstraints.tsx | Simplifies constraints header/actions while moving to shared profile flows. |
| src/features/package-profiles/api/useGetPagePackageProfile.ts | Avoids barrel import for hook to reduce coupling. |
| src/components/layout/InfoGrid/InfoGrid.tsx | Adds dense rendering option used by new shared blocks. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 300 out of 388 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
rubinaga
left a comment
There was a problem hiding this comment.
@gesquivelgaghi I made some comments here, mainly ones on the tests, I didnt comment on all files but if you could swap the mock usage especially for hooks or components to msw handlers it would be nice
| import useProfiles from "@/hooks/useProfiles"; | ||
| import { profiles as profileList } from "@/tests/mocks/profiles"; | ||
|
|
||
| vi.mock("@/hooks/usePageParams", () => ({ |
There was a problem hiding this comment.
do we need to do all these hook mocks? why not use msw handlers and mocks
There was a problem hiding this comment.
I removed the pageParams mock, but can't see a way to not need the useProfiles mock, since its a context hook so msw cant really help.
But I'm curious, why should we avoid mocking non-api hooks and other components? Tbh copilot generated the tests with everything mocked, and I did delete some particularly unnecessary ones before committing (though I clearly missed others, which I appreciate your comments for pointing those out). But I thought since these are unit tests, decoupling them from their dependencies isn't really a bad thing, so I left the ones I thought were useful/made the tests more maintainable.
There was a problem hiding this comment.
I think our unit tests arent strictly unit tests, more like integration tests. I believe it helps catch a bigger amount of bugs. And I think wherever we can use the actual components / hooks its better to use them and we have (most of) the tools in place for them. Like for usePageParams you can pass url parameters to renderWithProviders if you need to consume url parameters in the component. Obviously sometimes its inevitable to mock something but in general I think our hook mocks have been scoped to auth, account, environment stuff.
There was a problem hiding this comment.
makes sense, i removed the unnecessary mocks and left ones that significantly simplify the boilerplate needed
|
@gesquivelgaghi could you take a look at this re-rendering issue too please? I suspect its the Profile context causing the sidebar to re-render. It doesn't happen in |
| const { removeUpgradeProfileQuery } = useUpgradeProfiles(); | ||
|
|
||
| const removeProfile = async (params: RemoveProfileParams) => { | ||
| switch (type) { |
There was a problem hiding this comment.
All hooks are initialized regardless of type. Every call to this hook sets up mutation observers for all profile types, even though only one type is being used. This is unnecessary overhead that grows with each new profile type.
There was a problem hiding this comment.
Yeah, I'm aware of this issue but wasn't really sure how to bypass it. Maybe I can call the individual useRemove[Type]Profile hooks in the individual [Type]ProfilesPage and then store it in the ProfilesContext so it can be accessed inside the RemoveProfileModal
There was a problem hiding this comment.
that worked. its not super ideal, since now we're initializing the hook for the remove/archiveProfile before its needed, but at least its just for the profile the user's on
There was a problem hiding this comment.
The only way to do is separate hooks into useRemovePackageProfile, useRemoveRepositoryProfile, etc. This way each hook will already know the type. And put them into each "profile type" folder separately. We can still have a shared "profiles" folder, but for things that are different across profiles we should use subfolders for each profile type individually.
There was a problem hiding this comment.
The hooks already exist separately, the issue is they each need to be used within RemoveProfileModal (which is a general component for all profile types), and hooks can't be conditionally called.
What I did was instead call each one individually inside PackageProfilesPage, RepositoryProfilesPage etc, and then save the needed values to the ProfilesContext to be used by RemoveProfileModal without needing to invoke the hooks themselves there. The issue with this approach is that the useRemovePackageProfile hook will be initialized as soon as the user navigates to the package profiles page, regardless of whether or not they intend to remove a profile. But it'll be limited to the type of the page the user is on, which I think is an improvement on the previous solution
| name: string; | ||
| } | ||
|
|
||
| export type RemoveProfileParams = RemoveWithProfileNameParams & |
There was a problem hiding this comment.
I believe, this should be an intersection, not a union. It's either "id" or "name".
There was a problem hiding this comment.
i tried that first, but it leads to a typescript issue since this is used for the general removeProfile mutation that is assigned each individual profile's mutateAsync depending on the case. So it needs to include both name and id as required params or typescript will say that, for example, removeWslProfile can't be used as removeProfile because id: number is required for the first and optional for the latter.
There was a problem hiding this comment.
I think this issue will be gone too when we split hooks.
There was a problem hiding this comment.
I'd suggest moving this hooks inside the feature folder.
Summary
ProfilesList,ViewProfileSidePanel,ProfilesEmptyStatecomponents.Release Impact
According to the Landscape Server Release Cycle, this change will target the following release cycle:
main(Beta)Checklist
pnpm changesetand committed the resulting.mdfile.scripts/).Versioning Reminder
Important
This repository now uses CalVer ($YY.0M.Point.Patch$ ).
Please ensure your changeset description is clear, as it will be automatically added to the
CHANGELOG.mdupon merging tomain.