Wizard: Spread modules into row and handle adding/removing from the store

This adds `enabled_modules` to the store schema and handles adding/removing modules to both list of packages and list of enabled_modules.
This commit is contained in:
regexowl 2025-04-24 13:11:47 +02:00 committed by Klara Simickova
parent 2055f3b393
commit 524fd40673
3 changed files with 118 additions and 6 deletions

View file

@ -87,6 +87,9 @@ import {
addRecommendedRepository,
removeRecommendedRepository,
selectRecommendedRepositories,
addModule,
removeModule,
selectModules,
} from '../../../../store/wizardSlice';
import sortfn from '../../../../Utilities/sortfn';
import useDebounce from '../../../../Utilities/useDebounce';
@ -136,6 +139,7 @@ const Packages = () => {
const recommendedRepositories = useAppSelector(selectRecommendedRepositories);
const packages = useAppSelector(selectPackages);
const groups = useAppSelector(selectGroups);
const modules = useAppSelector(selectModules);
const { data: distroRepositories, isSuccess: isSuccessDistroRepositories } =
useGetArchitecturesQuery({
@ -620,7 +624,7 @@ const Packages = () => {
let transformedRecommendedData: IBPackageWithRepositoryInfo[] = [];
if (isSuccessDistroPackages) {
transformedDistroData = dataDistroPackages!.map((values) => ({
transformedDistroData = dataDistroPackages.map((values) => ({
name: values.package_name!,
summary: values.summary!,
repository: 'distro',
@ -629,7 +633,7 @@ const Packages = () => {
}
if (isSuccessCustomPackages) {
transformedCustomData = dataCustomPackages!.map((values) => ({
transformedCustomData = dataCustomPackages.map((values) => ({
name: values.package_name!,
summary: values.summary!,
repository: 'custom',
@ -637,9 +641,22 @@ const Packages = () => {
}));
}
let combinedPackageData = transformedDistroData.concat(
transformedCustomData
);
// Combine distribution and custom repositories packages
let combinedPackageData = transformedDistroData
.concat(transformedCustomData)
.flatMap((item) => {
// Spread modules into separate rows by application stream
if (item.sources && item.sources[0].type === 'module') {
return item.sources.map((source) => ({
name: item.name,
summary: item.summary,
repository: item.repository,
sources: [source],
}));
} else {
return [item];
}
});
if (
debouncedSearchTerm !== '' &&
@ -793,12 +810,27 @@ const Packages = () => {
setIsSelectingPackage(pkg);
} else {
dispatch(addPackage(pkg));
if (pkg.sources && pkg.sources[0].type === 'module') {
dispatch(
addModule({
name: pkg.sources[0].name || '',
stream: pkg.sources[0].stream || '',
})
);
}
setCurrentlyRemovedPackages((prev) =>
prev.filter((curr) => curr.name !== pkg.name)
);
}
} else {
dispatch(removePackage(pkg.name));
if (
pkg.sources &&
pkg.sources[0].type === 'module' &&
pkg.sources[0].name
) {
dispatch(removeModule(pkg.sources[0].name));
}
setCurrentlyRemovedPackages((last) => [...last, pkg]);
if (
isSuccessEpelRepo &&
@ -940,6 +972,39 @@ const Packages = () => {
const isGroupExpanded = (group: GroupWithRepositoryInfo['name']) =>
expandedGroups.includes(group);
const isPackageSelected = (pkg: IBPackageWithRepositoryInfo) => {
let isSelected = false;
if (!pkg.sources || pkg.sources[0].type === 'package') {
isSelected = packages.some((p) => p.name === pkg.name);
}
if (pkg.sources && pkg.sources[0] && pkg.sources[0].type === 'module') {
isSelected =
packages.some((p) => p.name === pkg.name) &&
modules.some(
(p) =>
pkg.sources && pkg.sources[0] && p.stream === pkg.sources[0].stream
);
}
return isSelected;
};
const isSelectDisabled = (pkg: IBPackageWithRepositoryInfo) => {
return (
(pkg.sources &&
pkg.sources[0].type === 'module' &&
modules.some(
(module) => pkg.sources && module.name === pkg.sources[0].name
) &&
!modules.some(
(p) => pkg.sources && p.stream === pkg.sources[0].stream
)) ||
false
);
};
const composePkgTable = () => {
let rows: ReactElement[] = [];
@ -1097,10 +1162,11 @@ const Packages = () => {
/>
<Td
select={{
isSelected: packages.some((p) => p.name === pkg.name),
isSelected: isPackageSelected(pkg),
rowIndex: rowIndex,
onSelect: (event, isSelecting) =>
handleSelect(pkg, rowIndex, isSelecting),
isDisabled: isSelectDisabled(pkg),
}}
/>
<Td>{pkg.name}</Td>

View file

@ -88,6 +88,7 @@ import {
selectTemplate,
selectSatelliteCaCertificate,
selectSatelliteRegistrationCommand,
selectModules,
} from '../../../store/wizardSlice';
import isRhel from '../../../Utilities/isRhel';
import { FileSystemConfigurationType } from '../steps/FileSystem';
@ -329,6 +330,7 @@ function commonRequestToState(
repository: '' as PackageRepository,
package_list: [],
})) || [],
enabled_modules: request.customizations.enabled_modules || [],
locale: {
languages: request.customizations.locale?.languages || [],
keyboard: request.customizations.locale?.keyboard || '',
@ -577,6 +579,7 @@ const getCustomizations = (state: RootState, orgID: string): Customizations => {
files: files.length > 0 ? files : undefined,
subscription: getSubscription(state, orgID),
packages: getPackages(state),
enabled_modules: getModules(state),
payload_repositories: getPayloadRepositories(state),
custom_repositories: getCustomRepositories(state),
openscap: getOpenscap(state),
@ -696,6 +699,16 @@ const getPackages = (state: RootState) => {
}
};
const getModules = (state: RootState) => {
const modules = selectModules(state);
if (modules.length > 0) {
return modules;
} else {
return undefined;
}
};
const getTimezone = (state: RootState) => {
const timezone = selectTimezone(state);
const ntpservers = selectNtpServers(state);

View file

@ -8,6 +8,7 @@ import type {
ImageRequest,
ImageTypes,
Locale,
Module,
Repository,
Timezone,
User,
@ -141,6 +142,7 @@ export type wizardState = {
redHatRepositories: Repository[];
};
packages: IBPackageWithRepositoryInfo[];
enabled_modules: Module[];
groups: GroupWithRepositoryInfo[];
services: {
enabled: string[];
@ -232,6 +234,7 @@ export const initialState: wizardState = {
redHatRepositories: [],
},
packages: [],
enabled_modules: [],
groups: [],
services: {
enabled: [],
@ -415,6 +418,10 @@ export const selectPackages = (state: RootState) => {
return state.wizard.packages;
};
export const selectModules = (state: RootState) => {
return state.wizard.enabled_modules;
};
export const selectGroups = (state: RootState) => {
return state.wizard.groups;
};
@ -808,6 +815,30 @@ export const wizardSlice = createSlice({
state.packages.splice(index, 1);
}
},
addModule: (state, action: PayloadAction<Module>) => {
const existingModuleIndex = state.enabled_modules.findIndex(
(module) => module.name === action.payload.name
);
if (existingModuleIndex !== -1) {
state.enabled_modules[existingModuleIndex] = action.payload;
} else {
state.enabled_modules.push(action.payload);
}
},
removeModule: (state, action: PayloadAction<Module['name']>) => {
const index = state.enabled_modules.findIndex(
(module) => module.name === action.payload
);
// count other packages from the same module
const pkgCount = state.packages.filter((pkg) =>
pkg.sources?.some((module) => module.name === action.payload)
);
// if the module exists and it's not connected to any packages, remove it
if (index !== -1 && pkgCount.length < 1) {
state.enabled_modules.splice(index, 1);
}
},
addGroup: (state, action: PayloadAction<GroupWithRepositoryInfo>) => {
const existingGrpIndex = state.groups.findIndex(
(grp) => grp.name === action.payload.name
@ -1129,6 +1160,8 @@ export const {
removeRecommendedRepository,
addPackage,
removePackage,
addModule,
removeModule,
addGroup,
removeGroup,
addLanguage,