Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
551c379
jest: add new entry into module name mapper
Starle21 Nov 24, 2025
de25016
clean: forgotten console.log in TagsFilter
Starle21 Jan 7, 2026
b622db2
move: StatusIcon and PackageCount under generic components
Starle21 Feb 17, 2026
2adedff
change: early return in EmptyTableState component
Starle21 Dec 7, 2025
4503af5
move: helpers for TemplateDetails into that folder
Starle21 Feb 17, 2026
450b6af
move: AddOrEditTemplate under features
Starle21 Feb 24, 2026
09f1044
extract: create and edit workflows
Starle21 Feb 17, 2026
52c68ac
extract: cancel modal behavior
Starle21 Feb 17, 2026
a35de2a
extract: choose step behavior
Starle21 Feb 17, 2026
4abe5af
extract: logic from DefineContentStep
Starle21 Feb 24, 2026
3cc2425
extract: logic from RedhatRepositoriesStep
Starle21 Feb 25, 2026
d3e7af8
extract: logic from CustomRepositoriesStep
Starle21 Mar 1, 2026
7621680
extract: logic from SetUpDateStep
Starle21 Feb 24, 2026
27e14bc
extract: logic from DetailStep
Starle21 Feb 25, 2026
3479c40
extract: logic from ReviewStep
Starle21 Feb 25, 2026
578352d
rename: AddOrEditTemplate -> TemplateModalBase
Starle21 Feb 25, 2026
2e4ba76
extract: defineContent logic from AddTemplateContext
Starle21 Feb 24, 2026
998e4a8
rename: templateHelpers -> repositoryURLs
Starle21 Feb 24, 2026
262fab8
change: data structure of repositoryURLs
Starle21 Feb 25, 2026
92ffb67
extract: edit use-case from AddTemplateContext
Starle21 Feb 24, 2026
5fc6073
extract: checkIsDisabledStep from AddTemplateContext
Starle21 Feb 11, 2026
ec7f361
extract: queryClient from AddTemplateContext
Starle21 Feb 24, 2026
3ab5c16
add: types for the workflow
Starle21 Feb 25, 2026
bd725a7
move: shared core functions into shared folder
Starle21 Feb 25, 2026
f7af956
change: storage in AddTemplateContext
Starle21 Mar 2, 2026
43e7a4e
add: temporary set state from old to new storage
Starle21 Feb 28, 2026
55e4887
add, change: checking isEmptyTemplateRequest in AddTemplateContext
Starle21 Mar 2, 2026
ab63b15
change: use new template store api in enableStep
Starle21 Feb 25, 2026
cb55886
change: use new template store api in createTemplate
Starle21 Feb 28, 2026
2f2d1d2
change: use new template store api in editTemplate confirm
Starle21 Feb 28, 2026
c26182c
change: use new template store api in reviewTemplateRequest
Starle21 Feb 19, 2026
76e0ece
change: use new template store api in describeTemplate
Starle21 Feb 24, 2026
36455e7
change: use new template store api in defineContent
Starle21 Feb 28, 2026
a75e938
change: use new template store api in selectSnapshots
Starle21 Mar 2, 2026
612b230
change: use new template store api in repositories selection
Starle21 Mar 2, 2026
dc33809
change: use new template store api in editTemplate initialize
Starle21 Feb 24, 2026
6af4411
delete: old template request store in AddTemplateContext
Starle21 Feb 23, 2026
37261ad
change: enableStep
Starle21 Feb 17, 2026
148003e
rename: AddTemplateContextProvider -> TemplateStore
Starle21 Feb 24, 2026
4355dad
change: AddOrEditTemplateModal
Starle21 Feb 17, 2026
8a54a0c
extract: createNewTemplate use-case
Starle21 Feb 28, 2026
298605e
extract: confirmEditTemplate and getTemplate use-cases
Starle21 Feb 28, 2026
f6fcdb1
extract, change: use-cases in DefineContent
Starle21 Feb 24, 2026
bf73c30
extract, change: use-case and UI in selectSnapshots
Starle21 Mar 1, 2026
86af607
extract: restrictFutureDates and props in SnapshotPicker
Starle21 Mar 1, 2026
ccbee4b
extract, change: formatTemplateReview, ReviewTemplateContent
Starle21 Feb 24, 2026
7dc0518
extract, change: validate use-cases in describeTemplate
Starle21 Feb 24, 2026
0457704
extract: sort repositories table
Starle21 Mar 1, 2026
3cc7f35
extract: toggleSelectedRepository in redhatRepositories
Starle21 Mar 1, 2026
417962c
extract, change: structure of RedhatRepositoriesStore
Starle21 Mar 2, 2026
f6eb6c9
extract: toggleOtherRepository in otherRepositories
Starle21 Mar 1, 2026
5656ad0
extract: refreshRepositories
Starle21 Mar 1, 2026
74c5f9b
extract, change: structure of CustomRepositoriesStore
Starle21 Mar 2, 2026
19e8a9d
documentation: notes for create template feature refactor
Starle21 Dec 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { RepositoryListServerResponse } from 'features/createAndEditTemplate/shared/types/types.repository';
import useErrorNotification from 'Hooks/useErrorNotification';
import { useQueryClient } from 'react-query';
import { ContentOrigin, getContentList } from 'services/Content/ContentApi';
import { CONTENT_LIST_KEY } from 'services/Content/ContentQueries';
import { FetchHardcodedRepositories } from '../core/ports';

const PAGE = 1;
const LIMIT = 10;
const SORTBY = '';
const CONTENT_ORIGIN = [ContentOrigin.REDHAT];

export const useFetchHardcodedRepositories = () => {
const queryClient = useQueryClient();
const errorNotifier = useErrorNotification();

const fetch: FetchHardcodedRepositories = async (filterData) => {
const { architecture, osVersion } = filterData;
const queryKey = [CONTENT_LIST_KEY, architecture, osVersion];

const formattedFilterData = {
availableForArch: architecture,
availableForVersion: osVersion,
};
const queryFn = () => getContentList(PAGE, LIMIT, formattedFilterData, SORTBY, CONTENT_ORIGIN);

const options = {
staleTime: 20000,
};

try {
const data = await queryClient.fetchQuery<RepositoryListServerResponse>(
queryKey,
queryFn,
options,
);
return data.data;
} catch (err) {
errorNotifier(
'Unable to get repositories list',
'An error occurred',
err,
'content-list-error',
);
return [];
}
};

return fetch;
};
25 changes: 25 additions & 0 deletions src/features/createAndEditTemplate/defineContent/core/ports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { FullRepository } from 'features/createAndEditTemplate/shared/types/types.repository';
import {
SystemConfigurationsLists,
SelectedSystemConfiguration,
SystemConfigurationsResponse,
} from './types';
import {
AllowedArchitecture,
AllowedOSVersion,
} from 'features/createAndEditTemplate/shared/types/types';
import { UseQueryResult } from 'react-query';

// input ports
export type GetRepositoryVersionsLists = () => SystemConfigurationsLists;
export type SelectArchitecture = (architecture: AllowedArchitecture) => void;
export type SelectOSVersion = (version: AllowedOSVersion) => void;
export type ChooseHardcodedUUIDs = (version: SelectedSystemConfiguration) => Promise<void>;

// output ports
export type FetchSystemConfigurations = () => UseQueryResult<SystemConfigurationsResponse>;
export type FetchHardcodedRepositories = (
version: SelectedSystemConfiguration,
) => Promise<FullRepository[]>;
// read from top level store - selectedArchitecture, selectedOSVersion
// set data in top level store - setArchitecture, setOSVersion, resetTemplateRequestContent
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useCallback } from 'react';

import { useTemplateRequestApi } from 'features/createAndEditTemplate/workflow/store/TemplateStore';
import { useFetchHardcodedRepositories } from '../../api/fetchHardcodedRepositories';
import { lookupUrls } from 'features/createAndEditTemplate/shared/core/lookupUrls';
import { filterHardcodedUUIDs } from '../domain/filterHardcodedUUIDs';
import { ChooseHardcodedUUIDs } from '../ports';

export const useChooseHardcodedUUIDs = () => {
const fetchHardcodedRepositories = useFetchHardcodedRepositories();
const { setHardcodedUUIDs } = useTemplateRequestApi();

const chooseHardcodedRedhatUUIDs: ChooseHardcodedUUIDs = async (version) => {
const repositories = await fetchHardcodedRepositories({
architecture: version.architecture,
osVersion: version.osVersion,
});
const hardcodedRedhatRepoUrls = lookupUrls(version);
const uuids = filterHardcodedUUIDs(repositories, hardcodedRedhatRepoUrls);
setHardcodedUUIDs(uuids);
};

return useCallback(chooseHardcodedRedhatUUIDs, []);
};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would use a different name here 😵‍💫
"systems" is a little miss-leading since that's something usually connected to Patch and actual systems, while this just gets the available template content config (repository parameters) options 💭

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useMemo } from 'react';
import { toDomain } from '../../api/versionsListToDomain';
import { GetRepositoryVersionsLists } from '../ports';
import { useRepositoryParams } from 'services/Content/ContentQueries';

export const useInitializeSystemsLists: GetRepositoryVersionsLists = () => {
const { data } = useRepositoryParams();

const filteredLists = useMemo(() => toDomain(data), [data]);

return filteredLists;
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,131 +2,115 @@ import {
useTemplateRequestApi,
useTemplateRequestState,
} from 'features/createAndEditTemplate/workflow/store/TemplateStore';
import { createContext, ReactNode, useContext, useEffect, useMemo, useState } from 'react';
import { ContentOrigin } from 'services/Content/ContentApi';
import { useContentListQuery, useRepositoryParams } from 'services/Content/ContentQueries';
import { createContext, ReactNode, useContext, useLayoutEffect, useMemo } from 'react';
import { useEditTemplateState } from 'features/createAndEditTemplate/editTemplate/store/EditTemplateStore';
import { lookupUrls } from 'features/createAndEditTemplate/shared/core/lookupUrls';
import { filterHardcodedUUIDs } from '../core/domain/filterHardcodedUUIDs';
import {
AllowedArchitecture,
AllowedOSVersion,
Architecture,
FirstEmpty,
HardcodedRepositoryUrls,
OSVersion,
} from 'features/createAndEditTemplate/shared/types/types';
import { toDomain } from '../api/versionsListToDomain';

export type SelectArchitecture = (architecture: AllowedArchitecture) => void;
export type SelectOSVersion = (version: AllowedOSVersion) => void;
import { useChooseHardcodedUUIDs } from '../core/use-cases/chooseHardcodedUUIDs';
import { useInitializeSystemsLists } from '../core/use-cases/initializeSystemsLists';
import { SelectArchitecture, SelectOSVersion } from '../core/ports';

type DefineContentApiType = {
architectures: Architecture[];
osVersions: OSVersion[];
selectedArchitecture: FirstEmpty<AllowedArchitecture>;
selectedOSVersion: FirstEmpty<AllowedOSVersion>;
onSelectArchitecture: SelectArchitecture;
onSelectOSVersion: SelectOSVersion;
};

const initialApi = {
onSelectArchitecture: () => {},
onSelectOSVersion: () => {},
};

type DefineContentStateType = {
selectedArchitecture: FirstEmpty<AllowedArchitecture>;
selectedOSVersion: FirstEmpty<AllowedOSVersion>;
isArchitectureItemSelected: (item: AllowedArchitecture) => boolean;
isOSVersionItemSelected: (item: AllowedOSVersion) => boolean;
};

const initialData = {
architectures: [],
osVersions: [],
const initialState = {
selectedArchitecture: undefined,
selectedOSVersion: undefined,
setVersionOpen: () => {},
setArchOpen: () => {},
onSelectArchitecture: () => {},
onSelectOSVersion: () => {},
isArchitectureItemSelected: () => false,
isOSVersionItemSelected: () => false,
};

const DefineContentApi = createContext<DefineContentApiType>(initialData);
export type SystemConfigurationsLists = {
architectures: Architecture[];
osVersions: OSVersion[];
};

export const initialSystemsLists = {
architectures: [],
osVersions: [],
};

const SystemConfigurationsLists = createContext<SystemConfigurationsLists>(initialSystemsLists);
export const useSystemLists = () => useContext(SystemConfigurationsLists);

const DefineContentApi = createContext<DefineContentApiType>(initialApi);
export const useDefineContentApi = () => useContext(DefineContentApi);

const DefineContentState = createContext<DefineContentStateType>(initialState);
export const useDefineContentState = () => useContext(DefineContentState);

type DefineContentStoreType = {
children: ReactNode;
};

export const DefineContentStore = ({ children }: DefineContentStoreType) => {
const [hardcodedRedhatRepositories, setHardcodeRepositories] = useState<HardcodedRepositoryUrls>([
'',
'',
]);

const { setHardcodedUUIDs, setOtherUUIDs, setArchitecture, setOSVersion } =
useTemplateRequestApi();
const { setArchitecture, setOSVersion, resetTemplateRequestContent } = useTemplateRequestApi();
const { selectedArchitecture, selectedOSVersion } = useTemplateRequestState();

const { uuid } = useEditTemplateState();

// >>>>>>>>
// get archs and versions to populate dropdowns
const { data: lists } = useRepositoryParams();

const systemsLists = useMemo(() => toDomain(lists), [lists]);
// <<<<<<<<

const onSelectArchitecture = (type) => {
setArchitecture(type);
};
const onSelectOSVersion = (type) => {
setOSVersion(type);
};

const isArchitectureItemSelected = (item) => item === selectedArchitecture;
const isOSVersionItemSelected = (item) => item === selectedOSVersion;

// >>>>>>>>
// 2. fetch those hardcoded repositories
const { data } = useContentListQuery(
1,
10,
{ urls: hardcodedRedhatRepositories },
'',
[ContentOrigin.REDHAT],
!!hardcodedRedhatRepositories.length,
);

// 1. when a user selects arch and os
// get urls of harcoded repos
useEffect(() => {
if (!!selectedArchitecture && !!selectedOSVersion) {
const hardcodedUrls = lookupUrls({
const { isEditTemplate } = useEditTemplateState();

const systemsLists = useInitializeSystemsLists();
const chooseHardcodedRedhatRepositories = useChooseHardcodedUUIDs();

const defineContentApi = useMemo(() => {
const onSelectArchitecture: SelectArchitecture = (type) => {
setArchitecture(type);
};
const onSelectOSVersion: SelectOSVersion = (type) => {
setOSVersion(type);
};
return { onSelectArchitecture, onSelectOSVersion };
}, []);

const defineContentState = useMemo(() => {
const isArchitectureItemSelected = (item) => item === selectedArchitecture;
const isOSVersionItemSelected = (item) => item === selectedOSVersion;
return {
selectedArchitecture,
selectedOSVersion,
isArchitectureItemSelected,
isOSVersionItemSelected,
};
}, [selectedArchitecture, selectedOSVersion]);

// automatically retrigger on arch or osversion change
useLayoutEffect(() => {
const isContentFilled = !!selectedArchitecture && !!selectedOSVersion;

if (isContentFilled && !isEditTemplate) {
resetTemplateRequestContent();
chooseHardcodedRedhatRepositories({
architecture: selectedArchitecture,
osVersion: selectedOSVersion,
});
if (hardcodedUrls) {
setHardcodeRepositories(hardcodedUrls);
}
if (!uuid) {
setOtherUUIDs([]);
}
}
}, [selectedArchitecture, selectedOSVersion, uuid]);

// 3. filter out hardcoded uuids
useEffect(() => {
if (data?.data?.length) {
const uuids = filterHardcodedUUIDs(data.data, hardcodedRedhatRepositories);
setHardcodedUUIDs(uuids);
}
}, [data?.data]);
// <<<<<<<<

const api = {
selectedArchitecture,
selectedOSVersion,
onSelectArchitecture,
onSelectOSVersion,
isArchitectureItemSelected,
isOSVersionItemSelected,
...systemsLists,
};

return <DefineContentApi.Provider value={api}>{children}</DefineContentApi.Provider>;
}, [selectedArchitecture, selectedOSVersion, isEditTemplate]);

return (
<SystemConfigurationsLists.Provider value={systemsLists}>
<DefineContentApi.Provider value={defineContentApi}>
<DefineContentState.Provider value={defineContentState}>
{children}
</DefineContentState.Provider>
</DefineContentApi.Provider>
</SystemConfigurationsLists.Provider>
);
};
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { render } from '@testing-library/react';
import { useDefineContentApi } from '../store/DefineContentStore';
import {
useDefineContentApi,
useDefineContentState,
useSystemLists,
} from '../store/DefineContentStore';
import { defaultTemplateItem } from 'testingHelpers';
import DefineContentStep from './DefineContentStep';
import { useEditTemplateState } from 'features/createAndEditTemplate/editTemplate/store/EditTemplateStore';

jest.mock('@src/features/createAndEditTemplate/defineContent/store/DefineContentStore', () => ({
useDefineContentApi: jest.fn(),
useSystemLists: jest.fn(),
useDefineContentState: jest.fn(),
}));

jest.mock('@src/features/createAndEditTemplate/editTemplate/store/EditTemplateStore', () => ({
Expand All @@ -14,17 +20,23 @@ jest.mock('@src/features/createAndEditTemplate/editTemplate/store/EditTemplateSt

it('expect DefineContentStep to render correctly', () => {
const mockDefineContentApi = {
onSelectArchitecture: () => {},
onSelectOSVersion: () => {},
};
const mockSystemsLists = {
architectures: [{ descriptor: 'aarch64', displayName: 'aarch64' }],
osVersions: [{ descriptor: '9', displayName: 'el9' }],
};
const mockDefineContentState = {
selectedArchitecture: 'aarch64',
selectedOSVersion: '9',
onSelectArchitecture: () => {},
onSelectOSVersion: () => {},
};
const mockEditTemplateState = {
isEditTemplate: false,
};
(useDefineContentApi as jest.Mock).mockImplementation(() => mockDefineContentApi);
(useSystemLists as jest.Mock).mockImplementation(() => mockSystemsLists);
(useDefineContentState as jest.Mock).mockImplementation(() => mockDefineContentState);
(useEditTemplateState as jest.Mock).mockImplementation(() => mockEditTemplateState);

const { getByText } = render(<DefineContentStep />);
Expand All @@ -42,17 +54,23 @@ it('expect DefineContentStep to render correctly', () => {

it('expect DefineContentStep to render with disabled inputs', () => {
const mockDefineContentApi = {
onSelectArchitecture: () => {},
onSelectOSVersion: () => {},
};
const mockSystemsLists = {
architectures: [{ descriptor: 'aarch64', displayName: 'aarch64' }],
osVersions: [{ descriptor: '9', displayName: 'el9' }],
};
const mockDefineContentState = {
selectedArchitecture: 'aarch64',
selectedOSVersion: '9',
onSelectArchitecture: () => {},
onSelectOSVersion: () => {},
};
const mockEditTemplateState = {
isEditTemplate: true,
};
(useDefineContentApi as jest.Mock).mockImplementation(() => mockDefineContentApi);
(useSystemLists as jest.Mock).mockImplementation(() => mockSystemsLists);
(useDefineContentState as jest.Mock).mockImplementation(() => mockDefineContentState);
(useEditTemplateState as jest.Mock).mockImplementation(() => mockEditTemplateState);

const { getByTestId } = render(<DefineContentStep />);
Expand Down
Loading