diff --git a/apps/ocp-plugin/console-extensions.json b/apps/ocp-plugin/console-extensions.json
index f2c12b4c2..1548cf6df 100644
--- a/apps/ocp-plugin/console-extensions.json
+++ b/apps/ocp-plugin/console-extensions.json
@@ -35,6 +35,16 @@
"section": "fctl"
}
},
+ {
+ "type": "console.navigation/href",
+ "properties": {
+ "id": "fctl-catalog",
+ "name": "%plugin__flightctl-plugin~Software Catalog%",
+ "href": "/edge/catalog",
+ "perspective": "acm",
+ "section": "fctl"
+ }
+ },
{
"type": "console.navigation/href",
"properties": {
@@ -87,6 +97,14 @@
"component": { "$codeRef": "FleetDetailsPage" }
}
},
+ {
+ "type": "console.page/route",
+ "properties": {
+ "exact": false,
+ "path": ["/edge/fleets/catalog/:fleetId/:catalogId/:itemId"],
+ "component": { "$codeRef": "CatalogEditFleetWizard" }
+ }
+ },
{
"type": "console.page/route",
"properties": {
@@ -111,6 +129,30 @@
"component": { "$codeRef": "EditDeviceWizardPage" }
}
},
+ {
+ "type": "console.page/route",
+ "properties": {
+ "exact": false,
+ "path": ["/edge/devices/catalog/:deviceId/:catalogId/:itemId"],
+ "component": { "$codeRef": "CatalogEditDeviceWizard" }
+ }
+ },
+ {
+ "type": "console.page/route",
+ "properties": {
+ "exact": true,
+ "path": ["/edge/catalog"],
+ "component": { "$codeRef": "CatalogPage" }
+ }
+ },
+ {
+ "type": "console.page/route",
+ "properties": {
+ "exact": false,
+ "path": ["/edge/catalog/install/:catalogId/:itemId"],
+ "component": { "$codeRef": "CatalogInstallWizard" }
+ }
+ },
{
"type": "console.page/route",
"properties": {
diff --git a/apps/ocp-plugin/package.json b/apps/ocp-plugin/package.json
index 705ce02a6..c5ec24822 100644
--- a/apps/ocp-plugin/package.json
+++ b/apps/ocp-plugin/package.json
@@ -28,7 +28,11 @@
"ResourceSyncToRepositoryPage": "./src/components/ResourceSyncs/ResourceSyncToRepositoryPage.tsx",
"EnrollmentRequestDetailsPage": "./src/components/EnrollmentRequests/EnrollmentRequestDetailsPage.tsx",
"appContext": "./src/components/AppContext/AppContext.tsx",
- "OverviewTab": "./src/components/OverviewTab/OverviewTab.tsx"
+ "OverviewTab": "./src/components/OverviewTab/OverviewTab.tsx",
+ "CatalogPage": "./src/components/Catalog/CatalogPage.tsx",
+ "CatalogInstallWizard": "./src/components/Catalog/CatalogInstallWizard.tsx",
+ "CatalogEditDeviceWizard": "./src/components/Catalog/CatalogEditDeviceWizard.tsx",
+ "CatalogEditFleetWizard": "./src/components/Catalog/CatalogEditFleetWizard.tsx"
},
"dependencies": {
"@console/pluginAPI": "*"
@@ -74,6 +78,7 @@
"@patternfly/react-icons": "^6.4.0",
"@patternfly/react-styles": "^6.4.0",
"@patternfly/react-table": "^6.4.0",
+ "@patternfly/react-topology": "^6.4.0",
"@types/react-redux": "^7.1.33",
"formik": "^2.4.5",
"fuzzysearch": "^1.0.3",
diff --git a/apps/ocp-plugin/src/components/AppContext/AppContext.tsx b/apps/ocp-plugin/src/components/AppContext/AppContext.tsx
index 87ff7f4a3..ac27850e7 100644
--- a/apps/ocp-plugin/src/components/AppContext/AppContext.tsx
+++ b/apps/ocp-plugin/src/components/AppContext/AppContext.tsx
@@ -64,6 +64,10 @@ const appRoutes = {
[ROUTE.AUTH_PROVIDER_CREATE]: '/',
[ROUTE.AUTH_PROVIDER_EDIT]: '/',
[ROUTE.AUTH_PROVIDER_DETAILS]: '/',
+ [ROUTE.CATALOG]: '/edge/catalog',
+ [ROUTE.CATALOG_INSTALL]: '/edge/catalog/install',
+ [ROUTE.CATALOG_FLEET_EDIT]: '/edge/fleets/catalog',
+ [ROUTE.CATALOG_DEVICE_EDIT]: '/edge/devices/catalog',
};
export const useValuesAppContext = (): AppContextProps => {
diff --git a/apps/ocp-plugin/src/components/Catalog/CatalogEditDeviceWizard.tsx b/apps/ocp-plugin/src/components/Catalog/CatalogEditDeviceWizard.tsx
new file mode 100644
index 000000000..d13133762
--- /dev/null
+++ b/apps/ocp-plugin/src/components/Catalog/CatalogEditDeviceWizard.tsx
@@ -0,0 +1,13 @@
+import * as React from 'react';
+import { EditDeviceWizard } from '@flightctl/ui-components/src/components/Catalog/EditWizard/EditWizard';
+import WithPageLayout from '../common/WithPageLayout';
+
+const CatalogEditDeviceWizard = () => {
+ return (
+
+
+
+ );
+};
+
+export default CatalogEditDeviceWizard;
diff --git a/apps/ocp-plugin/src/components/Catalog/CatalogEditFleetWizard.tsx b/apps/ocp-plugin/src/components/Catalog/CatalogEditFleetWizard.tsx
new file mode 100644
index 000000000..4386c248d
--- /dev/null
+++ b/apps/ocp-plugin/src/components/Catalog/CatalogEditFleetWizard.tsx
@@ -0,0 +1,13 @@
+import * as React from 'react';
+import { EditFleetWizard } from '@flightctl/ui-components/src/components/Catalog/EditWizard/EditWizard';
+import WithPageLayout from '../common/WithPageLayout';
+
+const CatalogEditFleetWizard = () => {
+ return (
+
+
+
+ );
+};
+
+export default CatalogEditFleetWizard;
diff --git a/apps/ocp-plugin/src/components/Catalog/CatalogInstallWizard.tsx b/apps/ocp-plugin/src/components/Catalog/CatalogInstallWizard.tsx
new file mode 100644
index 000000000..b0befa876
--- /dev/null
+++ b/apps/ocp-plugin/src/components/Catalog/CatalogInstallWizard.tsx
@@ -0,0 +1,13 @@
+import * as React from 'react';
+import InstallWizard from '@flightctl/ui-components/src/components/Catalog/InstallWizard/InstallWizard';
+import WithPageLayout from '../common/WithPageLayout';
+
+const OcpInstallWizard = () => {
+ return (
+
+
+
+ );
+};
+
+export default OcpInstallWizard;
diff --git a/apps/ocp-plugin/src/components/Catalog/CatalogPage.tsx b/apps/ocp-plugin/src/components/Catalog/CatalogPage.tsx
new file mode 100644
index 000000000..fd34d2533
--- /dev/null
+++ b/apps/ocp-plugin/src/components/Catalog/CatalogPage.tsx
@@ -0,0 +1,13 @@
+import * as React from 'react';
+import CatalogPage from '@flightctl/ui-components/src/components/Catalog/CatalogPage';
+import WithPageLayout from '../common/WithPageLayout';
+
+const OcpCatalogPage = () => {
+ return (
+
+
+
+ );
+};
+
+export default OcpCatalogPage;
diff --git a/apps/ocp-plugin/src/typings.d.ts b/apps/ocp-plugin/src/typings.d.ts
new file mode 100644
index 000000000..77b9725af
--- /dev/null
+++ b/apps/ocp-plugin/src/typings.d.ts
@@ -0,0 +1,15 @@
+declare module '*.png';
+declare module '*.jpg';
+declare module '*.jpeg';
+declare module '*.gif';
+declare module '*.svg' {
+ const content: string;
+ export default content;
+}
+declare module '*.css';
+declare module '*.wav';
+declare module '*.mp3';
+declare module '*.m4a';
+declare module '*.rdf';
+declare module '*.ttl';
+declare module '*.pdf';
diff --git a/apps/ocp-plugin/src/utils/apiCalls.ts b/apps/ocp-plugin/src/utils/apiCalls.ts
index 31b26e3fb..1f16d8c52 100644
--- a/apps/ocp-plugin/src/utils/apiCalls.ts
+++ b/apps/ocp-plugin/src/utils/apiCalls.ts
@@ -16,7 +16,7 @@ declare global {
}
}
-type Api = 'flightctl' | 'imagebuilder' | 'alerts';
+type Api = 'flightctl' | 'imagebuilder' | 'alerts' | 'catalog';
const addRequiredHeaders = (options: RequestInit, api?: Api): RequestInit => {
const token = getCSRFToken();
@@ -47,6 +47,8 @@ export const uiProxy = `${window.location.protocol}//${apiServer}`;
const flightCtlAPI = `${uiProxy}/api/flightctl`;
const alertsAPI = `${uiProxy}/api/alerts`;
const imageBuilderPathRegex = /^image(builds|exports)/;
+const catalogPathRegex = /^(catalogs|catalogitems)/;
+
export const wsEndpoint = `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${apiServer}`;
export const fetchUiProxy = async (endpoint: string, requestInit: RequestInit): Promise => {
@@ -62,6 +64,12 @@ const getFullApiUrl = (path: string): { api: Api; url: string } => {
if (imageBuilderPathRegex.test(path)) {
return { api: 'imagebuilder', url: `${uiProxy}/api/imagebuilder/api/v1/${path}` };
}
+ if (catalogPathRegex.test(path)) {
+ return {
+ api: 'catalog',
+ url: `${flightCtlAPI}/api/v1/${path}`,
+ };
+ }
return { api: 'flightctl', url: `${flightCtlAPI}/api/v1/${path}` };
};
diff --git a/apps/ocp-plugin/tsconfig.json b/apps/ocp-plugin/tsconfig.json
index 04e7b8b01..cffff5bf3 100644
--- a/apps/ocp-plugin/tsconfig.json
+++ b/apps/ocp-plugin/tsconfig.json
@@ -24,6 +24,7 @@
"@flightctl/ui-components/*": ["../../libs/ui-components/*"],
"@flightctl/types": ["../../libs/types"],
"@flightctl/types/imagebuilder": ["../../libs/types/imagebuilder"],
+ "@flightctl/types/alpha": ["../../libs/types/alpha"],
},
},
"include": ["**/*.ts", "**/*.tsx", "**/*.jsx", "**/*.js", "**/*.json"],
diff --git a/apps/ocp-plugin/webpack.config.ts b/apps/ocp-plugin/webpack.config.ts
index 22d7b00f4..13bc22ab2 100644
--- a/apps/ocp-plugin/webpack.config.ts
+++ b/apps/ocp-plugin/webpack.config.ts
@@ -50,7 +50,15 @@ const config: Configuration & {
use: ['style-loader', 'css-loader'],
},
{
- test: /\.(png|jpg|jpeg|gif|svg|woff2?|ttf|eot|otf)(\?.*$|$)/,
+ test: /\.(jpg|jpeg|png|gif|svg)$/i,
+ include: [path.resolve(__dirname, '../../libs/ui-components/assets')],
+ type: 'asset/resource',
+ generator: {
+ filename: 'assets/[name].[ext]',
+ },
+ },
+ {
+ test: /\.(woff2?|ttf|eot|otf)(\?.*$|$)/,
loader: 'file-loader',
options: {
name: 'assets/[name].[ext]',
diff --git a/apps/standalone/package.json b/apps/standalone/package.json
index c0a2a44f0..d9171df7a 100644
--- a/apps/standalone/package.json
+++ b/apps/standalone/package.json
@@ -47,6 +47,7 @@
"@patternfly/react-icons": "^6.4.0",
"@patternfly/react-styles": "^6.4.0",
"@patternfly/react-table": "^6.4.0",
+ "@patternfly/react-topology": "^6.4.0",
"formik": "^2.4.5",
"fuzzysearch": "^1.0.3",
"i18next": "^21.8.14",
diff --git a/apps/standalone/src/app/routes.tsx b/apps/standalone/src/app/routes.tsx
index 3ef58cb9f..c36ca379e 100644
--- a/apps/standalone/src/app/routes.tsx
+++ b/apps/standalone/src/app/routes.tsx
@@ -84,6 +84,22 @@ const CreateImageBuildWizard = React.lazy(
() => import('@flightctl/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/CreateImageBuildWizard'),
);
+const CatalogPage = React.lazy(() => import('@flightctl/ui-components/src/components/Catalog/CatalogPage'));
+const CatalogInstallWizard = React.lazy(
+ () => import('@flightctl/ui-components/src/components/Catalog/InstallWizard/InstallWizard'),
+);
+const CatalogEditFleetWizard = React.lazy(() =>
+ import('@flightctl/ui-components/src/components/Catalog/EditWizard/EditWizard').then((module) => ({
+ default: module.EditFleetWizard,
+ })),
+);
+
+const CatalogEditDeviceWizard = React.lazy(() =>
+ import('@flightctl/ui-components/src/components/Catalog/EditWizard/EditWizard').then((module) => ({
+ default: module.EditDeviceWizard,
+ })),
+);
+
export type ExtendedRouteObject = RouteObject & {
title?: string;
showInNav?: boolean;
@@ -215,6 +231,15 @@ const getAppRoutes = (t: TFunction): ExtendedRouteObject[] => [
),
},
+ {
+ path: 'catalog/:fleetId/:catalogId/:itemId',
+ title: t('Edit Fleet'),
+ element: (
+
+
+
+ ),
+ },
{
path: ':fleetId/*',
title: t('Fleet Details'),
@@ -264,6 +289,40 @@ const getAppRoutes = (t: TFunction): ExtendedRouteObject[] => [
),
},
+ {
+ path: 'catalog/:deviceId/:catalogId/:itemId',
+ title: t('Edit device'),
+ element: (
+
+
+
+ ),
+ },
+ ],
+ },
+ {
+ path: '/catalog',
+ title: t('Software Catalog'),
+ showInNav: true,
+ children: [
+ {
+ index: true,
+ title: t('Software Catalog'),
+ element: (
+
+
+
+ ),
+ },
+ {
+ path: 'install/:catalogId/:itemId',
+ title: t('Install Catalog item'),
+ element: (
+
+
+
+ ),
+ },
],
},
{
diff --git a/apps/standalone/src/app/utils/apiCalls.ts b/apps/standalone/src/app/utils/apiCalls.ts
index 3014c1a16..a5dd83f79 100644
--- a/apps/standalone/src/app/utils/apiCalls.ts
+++ b/apps/standalone/src/app/utils/apiCalls.ts
@@ -16,6 +16,7 @@ const flightCtlAPI = `${window.location.protocol}//${apiServer}/api/flightctl`;
const uiProxyAPI = `${window.location.protocol}//${apiServer}/api`;
const imageBuilderPathRegex = /^image(builds|exports)/;
+const catalogPathRegex = /^(catalogs|catalogitems)/;
export const loginAPI = `${window.location.protocol}//${apiServer}/api/login`;
export const wsEndpoint = `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${apiServer}`;
@@ -45,13 +46,19 @@ export const fetchUiProxy = async (endpoint: string, requestInit: RequestInit):
return await fetch(`${uiProxyAPI}/${endpoint}`, options);
};
-const getFullApiUrl = (path: string): { api: 'flightctl' | 'imagebuilder' | 'alerts'; url: string } => {
+const getFullApiUrl = (path: string): { api: 'flightctl' | 'imagebuilder' | 'alerts' | 'catalog'; url: string } => {
if (path.startsWith('alerts')) {
return { api: 'alerts', url: `${uiProxyAPI}/alerts/api/v2/${path}` };
}
if (imageBuilderPathRegex.test(path)) {
return { api: 'imagebuilder', url: `${uiProxyAPI}/imagebuilder/api/v1/${path}` };
}
+ if (catalogPathRegex.test(path)) {
+ return {
+ api: 'catalog',
+ url: `${flightCtlAPI}/api/v1/${path}`,
+ };
+ }
return { api: 'flightctl', url: `${flightCtlAPI}/api/v1/${path}` };
};
diff --git a/apps/standalone/tsconfig.json b/apps/standalone/tsconfig.json
index e248fbcea..ecb4ee2c5 100644
--- a/apps/standalone/tsconfig.json
+++ b/apps/standalone/tsconfig.json
@@ -25,6 +25,7 @@
"@flightctl/ui-components/*": ["../../libs/ui-components/*"],
"@flightctl/types": ["../../libs/types"],
"@flightctl/types/imagebuilder": ["../../libs/types/imagebuilder"],
+ "@flightctl/types/alpha": ["../../libs/types/alpha"],
},
},
"include": ["**/*.ts", "**/*.tsx", "**/*.jsx", "**/*.js", "**/*.json"],
diff --git a/apps/standalone/webpack.config.ts b/apps/standalone/webpack.config.ts
index 9a6f4bbac..7422c1082 100644
--- a/apps/standalone/webpack.config.ts
+++ b/apps/standalone/webpack.config.ts
@@ -109,6 +109,7 @@ const config: Configuration & {
include: [
path.resolve(__dirname, 'src'),
path.resolve(__dirname, 'src/assets/images'),
+ path.resolve(__dirname, '../../libs/ui-components/assets'),
path.resolve(__dirname, 'node_modules/patternfly'),
path.resolve(__dirname, 'node_modules/@patternfly/patternfly/assets/images'),
path.resolve(__dirname, 'node_modules/@patternfly/react-styles/css/assets/images'),
@@ -127,15 +128,6 @@ const config: Configuration & {
),
],
type: 'asset/inline',
- use: [
- {
- options: {
- limit: 5000,
- outputPath: 'images',
- name: '[name].[ext]',
- },
- },
- ],
},
],
},
diff --git a/libs/i18n/locales/en/translation.json b/libs/i18n/locales/en/translation.json
index 04ba5a946..f04be819c 100644
--- a/libs/i18n/locales/en/translation.json
+++ b/libs/i18n/locales/en/translation.json
@@ -36,6 +36,9 @@
"Device": "Device",
"Devices": "Devices",
"Edit device": "Edit device",
+ "Software Catalog": "Software Catalog",
+ "Catalog": "Catalog",
+ "Install Catalog item": "Install Catalog item",
"Image builds": "Image builds",
"Build new image": "Build new image",
"Duplicate image build": "Duplicate image build",
@@ -184,6 +187,145 @@
"Field": "Field",
"Value": "Value",
"Close": "Close",
+ "Select {{ name }}": "Select {{ name }}",
+ "Provided by {{provider}}": "Provided by {{provider}}",
+ "Deprecated": "Deprecated",
+ "Resize panel": "Resize panel",
+ "Data catalog item can be deployed as part of an application.": "Data catalog item can be deployed as part of an application.",
+ "Deploy": "Deploy",
+ "Provider": "Provider",
+ "N/A": "N/A",
+ "Documentation URL": "Documentation URL",
+ "Homepage": "Homepage",
+ "Description": "Description",
+ "Readme": "Readme",
+ "No results found": "No results found",
+ "No catalog items yet": "No catalog items yet",
+ "No catalog items match the selected filters or search. Try adjusting the category or search.": "No catalog items match the selected filters or search. Try adjusting the category or search.",
+ "Catalog items are applications and system images you can deploy to your devices.": "Catalog items are applications and system images you can deploy to your devices.",
+ "Learn about catalogs": "Learn about catalogs",
+ "Operating system": "Operating system",
+ "Application": "Application",
+ "Container": "Container",
+ "Helm": "Helm",
+ "Quadlet": "Quadlet",
+ "Compose": "Compose",
+ "Data": "Data",
+ "Category": "Category",
+ "Search by name": "Search by name",
+ "Update": "Update",
+ "Version": "Version",
+ "Review": "Review",
+ "Version must be selected": "Version must be selected",
+ "Application name is required": "Application name is required",
+ "Application with the same name already exists.": "Application with the same name already exists.",
+ "Version {{version}} not found": "Version {{version}} not found",
+ "Failed to load catalog item": "Failed to load catalog item",
+ "Failed to load device": "Failed to load device",
+ "Failed to load fleet": "Failed to load fleet",
+ "Loading": "Loading",
+ "Failed to find operating system": "Failed to find operating system",
+ "Failed to find application": "Failed to find application",
+ "Software catalog": "Software catalog",
+ "Deploy {{ name }}": "Deploy {{ name }}",
+ "Edit {{name}}": "Edit {{name}}",
+ "Return to device catalog": "Return to device catalog",
+ "Return to fleet catalog": "Return to fleet catalog",
+ "Review update specifications": "Review update specifications",
+ "Review deployment specifications": "Review deployment specifications",
+ "Update specifications": "Update specifications",
+ "Installation specifications": "Installation specifications",
+ "Channel": "Channel",
+ "Failed to update": "Failed to update",
+ "Failed to deploy": "Failed to deploy",
+ "Configuration is not valid": "Configuration is not valid",
+ "The current version is deprecated": "The current version is deprecated",
+ "The selected version is deprecated": "The selected version is deprecated",
+ "Version update": "Version update",
+ "Current channel": "Current channel",
+ "Target channel": "Target channel",
+ "Current version": "Current version",
+ "Target version": "Target version",
+ "Up to date": "Up to date",
+ "{{ channel }} channel": "{{ channel }} channel",
+ "Deployment specifications": "Deployment specifications",
+ "Update available": "Update available",
+ "Version: {{version}}, Channel: {{channel}}": "Version: {{version}}, Channel: {{channel}}",
+ "Loading installed software": "Loading installed software",
+ "Deployed Software": "Deployed Software",
+ "No software deployed": "No software deployed",
+ "Select an operating system or application from the catalog below.": "Select an operating system or application from the catalog below.",
+ "operating system": "operating system",
+ "application": "application",
+ "Specifications": "Specifications",
+ "Select target": "Select target",
+ "Application configuration": "Application configuration",
+ "Target must be selected": "Target must be selected",
+ "Channel must be selected": "Channel must be selected",
+ "Device must be selected": "Device must be selected",
+ "Fleet must be selected": "Fleet must be selected",
+ "Failed to find requested version {{version}}": "Failed to find requested version {{version}}",
+ "Loading catalog item": "Loading catalog item",
+ "Install {{name}}": "Install {{name}}",
+ "Application name": "Application name",
+ "Application name must be unique.": "Application name must be unique.",
+ "Configure via:": "Configure via:",
+ "Form view": "Form view",
+ "YAML view": "YAML view",
+ "Form view is disabled for this application because the schema is not available": "Form view is disabled for this application because the schema is not available",
+ "Note: Some fields may not be represented in this form view. Please select \"YAML view\" for full control.": "Note: Some fields may not be represented in this form view. Please select \"YAML view\" for full control.",
+ "Fleet update": "Fleet update",
+ "This will deploy the OS <1>{osImageName}1> for all <3>({numOfDevices})3> devices in the <5>{values.fleet?.metadata.name}5> fleet. Devices will download and apply the update according to the configured update policies.": "This will deploy the OS <1>{osImageName}1> for all <3>({numOfDevices})3> devices in the <5>{values.fleet?.metadata.name}5> fleet. Devices will download and apply the update according to the configured update policies.",
+ "You are about to update OS <1>{osImageName}1>. This will update the OS image for all <4>({numOfDevices})4> devices in the <6>{values.fleet?.metadata.name}6> fleet. Devices will download and apply the update according to the configured update policies.": "You are about to update OS <1>{osImageName}1>. This will update the OS image for all <4>({numOfDevices})4> devices in the <6>{values.fleet?.metadata.name}6> fleet. Devices will download and apply the update according to the configured update policies.",
+ "Existing OS image detected": "Existing OS image detected",
+ "You are about to replace OS with <1>{osImageName}1>. This will update the OS image for all <4>({numOfDevices})4> devices in the <6>{values.fleet?.metadata.name}6> fleet. Devices will download and apply the update according to the configured update policies.": "You are about to replace OS with <1>{osImageName}1>. This will update the OS image for all <4>({numOfDevices})4> devices in the <6>{values.fleet?.metadata.name}6> fleet. Devices will download and apply the update according to the configured update policies.",
+ "Device update": "Device update",
+ "This will deploy the OS <1>{osImageName}1>. Device will download and apply the update according to the configured update policies.": "This will deploy the OS <1>{osImageName}1>. Device will download and apply the update according to the configured update policies.",
+ "You are about to update OS with <1>{osImageName}1>. Device will download and apply the update according to the configured update policies.": "You are about to update OS with <1>{osImageName}1>. Device will download and apply the update according to the configured update policies.",
+ "You are about to replace OS with <1>{osImageName}1>. Device will download and apply the update according to the configured update policies.": "You are about to replace OS with <1>{osImageName}1>. Device will download and apply the update according to the configured update policies.",
+ "Target": "Target",
+ "Target type": "Target type",
+ "Fleet": "Fleet",
+ "Select device": "Select device",
+ "Search by name or alias": "Search by name or alias",
+ "Enrolled devices table": "Enrolled devices table",
+ "Select fleet": "Select fleet",
+ "Fleets table": "Fleets table",
+ "Unknown": "Unknown",
+ "OpenShift Virtualization": "OpenShift Virtualization",
+ "Bare Metal": "Bare Metal",
+ "Amazon Web Services": "Amazon Web Services",
+ "Anaconda Installer": "Anaconda Installer",
+ "Google Cloud": "Google Cloud",
+ "KVM/custom cloud import": "KVM/custom cloud import",
+ "Microsoft Hyper-V": "Microsoft Hyper-V",
+ "VMware vSphere": "VMware vSphere",
+ "Cloud native": "Cloud native",
+ "Deployment target": "Deployment target",
+ "No items": "No items",
+ "Show readme": "Show readme",
+ "This version is deprecated": "This version is deprecated",
+ "You do not have permissions to list fleets": "You do not have permissions to list fleets",
+ "You do not have permissions to edit fleets": "You do not have permissions to edit fleets",
+ "No fleet is available": "No fleet is available",
+ "You do not have permissions to list devices": "You do not have permissions to list devices",
+ "You do not have permissions to edit devices": "You do not have permissions to edit devices",
+ "No device is available": "No device is available",
+ "Loading targets": "Loading targets",
+ "Existing Fleet": "Existing Fleet",
+ "Install to all devices in a fleet": "Install to all devices in a fleet",
+ "Existing Device": "Existing Device",
+ "Install to a single fleetless device": "Install to a single fleetless device",
+ "New Device": "New Device",
+ "Provision a brand new, unenrolled device": "Provision a brand new, unenrolled device",
+ "Update configuration successful": "Update configuration successful",
+ "Device will download and apply the update according to the configured update policies.": "Device will download and apply the update according to the configured update policies.",
+ "Devices will download and apply the update according to the configured update policies.": "Devices will download and apply the update according to the configured update policies.",
+ "Return to catalog": "Return to catalog",
+ "View device": "View device",
+ "View fleet": "View fleet",
+ "Not a valid configuration": "Not a valid configuration",
+ "OS image": "OS image",
"No devices": "No devices",
"Restricted Access": "Restricted Access",
"You don't have access to this section.": "You don't have access to this section.",
@@ -334,7 +476,6 @@
"You can add devices and label them to match fleets, or you can <2>start with a fleet2> and add devices into it.": "You can add devices and label them to match fleets, or you can <2>start with a fleet2> and add devices into it.",
"You can add devices and label them to match fleets": "You can add devices and label them to match fleets",
"No decommissioning or decommissioned devices here!": "No decommissioning or decommissioned devices here!",
- "Fleet": "Fleet",
"Name / Alias": "Name / Alias",
"Clear all filters": "Clear all filters",
"Searching...": "Searching...",
@@ -345,7 +486,6 @@
"Labels and fleets": "Labels and fleets",
"Filter by labels and fleets": "Filter by labels and fleets",
"Decommission devices": "Decommission devices",
- "Enrolled devices table": "Enrolled devices table",
"Device is non-editable": "Device is non-editable",
"General info": "General info",
"Device template": "Device template",
@@ -355,7 +495,6 @@
"This port mapping already exists": "This port mapping already exists",
"Port mapping must be in format \"hostPort:containerPort\"": "Port mapping must be in format \"hostPort:containerPort\"",
"Invalid port values": "Invalid port values",
- "Application name": "Application name",
"If not specified, the image name will be used. Application name must be unique.": "If not specified, the image name will be used. Application name must be unique.",
"Image": "Image",
"Provide a valid image reference": "Provide a valid image reference",
@@ -409,6 +548,7 @@
"Rootless user identity": "Rootless user identity",
"The recommended user identity is '{{ runAsUser }}'. To specify a custom user identity, edit the application configuration via YAML or CLI.": "The recommended user identity is '{{ runAsUser }}'. To specify a custom user identity, edit the application configuration via YAML or CLI.",
"Application {{ appNum }}": "Application {{ appNum }}",
+ "Application is managed by Software Catalog": "Application is managed by Software Catalog",
"Application type": "Application type",
"Select an application type": "Select an application type",
"Definition source": "Definition source",
@@ -499,6 +639,7 @@
"System image": "System image",
"The target system image for this fleet's devices.": "The target system image for this fleet's devices.",
"The target system image for this device.": "The target system image for this device.",
+ "System image is managed by Software Catalog": "System image is managed by Software Catalog",
"Must be a reference to a bootable container image (such as \"quay.io//my-rhel-with-fc-agent:\"). If you do not want to manage your OS from Edge management, leave this field empty.": "Must be a reference to a bootable container image (such as \"quay.io//my-rhel-with-fc-agent:\"). If you do not want to manage your OS from Edge management, leave this field empty.",
"Use basic configurations": "Use basic configurations",
"Advanced configurations": "Advanced configurations",
@@ -518,6 +659,16 @@
"Maximum unavailable devices: {{ maxUnavailable }}": "Maximum unavailable devices: {{ maxUnavailable }}",
"Add service": "Add service",
"Tracked systemd services": "Tracked systemd services",
+ "Items": "Items",
+ "Delete item": "Delete item",
+ "Add item": "Add item",
+ "Choose asset from catalog": "Choose asset from catalog",
+ "Clear all filters and try again.": "Clear all filters and try again.",
+ "No assets available in catalog": "No assets available in catalog",
+ "There are no asset catalog items to choose from. Add assets to your catalogs to select them here.": "There are no asset catalog items to choose from. Add assets to your catalogs to select them here.",
+ "Select": "Select",
+ "Enter image reference or choose from catalog": "Enter image reference or choose from catalog",
+ "Choose from catalog": "Choose from catalog",
"Approve": "Approve",
"Certificate signing request": "Certificate signing request",
"A PEM-encoded PKCS#10 certificate signing request.": "A PEM-encoded PKCS#10 certificate signing request.",
@@ -531,7 +682,6 @@
"Created": "Created",
"Devices pending approval": "Devices pending approval",
"Table for devices pending approval": "Table for devices pending approval",
- "Search by name": "Search by name",
"{{ count }} devices pending approval_one": "{{ count }} device pending approval",
"{{ count }} devices pending approval_other": "{{ count }} devices pending approval",
"No matching events": "No matching events",
@@ -600,9 +750,7 @@
"Resourcesync is not accessible": "Resourcesync is not accessible",
"Resourcesync new commit detected": "Resourcesync new commit detected",
"Resource": "Resource",
- "Review": "Review",
"Review and create": "Review and create",
- "View fleet": "View fleet",
"Edit fleet": "Edit fleet",
"Create fleet": "Create fleet",
"Failed to determine the number of selected devices": "Failed to determine the number of selected devices",
@@ -698,7 +846,6 @@
"Fleets allow you to edit and update your devices at once.": "Fleets allow you to edit and update your devices at once.",
"To get started, create a new fleet or import an existing configuration.": "To get started, create a new fleet or import an existing configuration.",
"Delete fleets": "Delete fleets",
- "Fleets table": "Fleets table",
"Repository is required": "Repository is required",
"Import": "Import",
"Select or create repository": "Select or create repository",
@@ -717,10 +864,8 @@
"Target revision": "Target revision",
"Fleets will appear in the fleets table list and their status will be reflecting the resource sync process status. After a few minutes, they should be synced and enabled.": "Fleets will appear in the fleets table list and their status will be reflecting the resource sync process status. After a few minutes, they should be synced and enabled.",
"Invalid {{ itemType }}": "Invalid {{ itemType }}",
- "Add item": "Add item",
"Resolved": "Resolved",
"Name must be unique": "Name must be unique",
- "Unknown": "Unknown",
"Accessible": "Accessible",
"Not accessible": "Not accessible",
"Missing repository": "Missing repository",
@@ -786,7 +931,6 @@
"YAML content is invalid.": "YAML content is invalid.",
"Name is required for quadlet applications.": "Name is required for quadlet applications.",
"Name is required for compose applications.": "Name is required for compose applications.",
- "Application name must be unique.": "Application name must be unique.",
"Name is required, another application uses the same image.": "Name is required, another application uses the same image.",
"Cannot be decimal": "Cannot be decimal",
"Percentage must be between 1 and 100.": "Percentage must be between 1 and 100.",
@@ -1004,7 +1148,6 @@
"Select your preferred provider and use the command to log in to the Flight Control CLI.": "Select your preferred provider and use the command to log in to the Flight Control CLI.",
"Use the following command to log in to the Flight Control CLI:": "Use the following command to log in to the Flight Control CLI:",
"Copy Login Command": "Copy Login Command",
- "Provider": "Provider",
"Approve pending devices": "Approve pending devices",
"Make sure you recognise and expect the following devices before approving them. Are you sure you want to approve the listed devices?": "Make sure you recognise and expect the following devices before approving them. Are you sure you want to approve the listed devices?",
"Alias devices using a custom template. Add a number using": "Alias devices using a custom template. Add a number using",
@@ -1218,15 +1361,13 @@
"Suspended devices detected": "Suspended devices detected",
"<0>Warning:0> Please review this fleet's configuration before taking action. Resuming a device will cause it to apply the current specification, which may be older than what is on the device.": "<0>Warning:0> Please review this fleet's configuration before taking action. Resuming a device will cause it to apply the current specification, which may be older than what is on the device.",
"<0>Warning:0> Please review device configurations before taking action. Resuming a device will cause it to apply the current specification, which may be older than what is on the device.": "<0>Warning:0> Please review device configurations before taking action. Resuming a device will cause it to apply the current specification, which may be older than what is on the device.",
- "No results found": "No results found",
- "Clear all filters and try again.": "Clear all filters and try again.",
"Select all rows": "Select all rows",
+ "Row select": "Row select",
"Expand row": "Expand row",
"{page} of <2>{totalPages}2>": "{page} of <2>{totalPages}2>",
"{{ numberOfItems }} items": "{{ numberOfItems }} items",
"Waiting for terminal session to open...": "Waiting for terminal session to open...",
"Architecture": "Architecture",
- "Operating system": "Operating system",
"Agent version": "Agent version",
"Distro": "Distro",
"Hostname": "Hostname",
@@ -1247,9 +1388,6 @@
"Helm application": "Helm application",
"Compose application": "Compose application",
"Single Container": "Single Container",
- "Quadlet": "Quadlet",
- "Compose": "Compose",
- "Helm": "Helm",
"OpenShift": "OpenShift",
"Kubernetes": "Kubernetes",
"Ansible Automation Platform": "Ansible Automation Platform",
diff --git a/libs/types/alpha/index.ts b/libs/types/alpha/index.ts
new file mode 100644
index 000000000..a211d3ede
--- /dev/null
+++ b/libs/types/alpha/index.ts
@@ -0,0 +1,24 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+export { ApiVersion } from './models/ApiVersion';
+export type { Catalog } from './models/Catalog';
+export type { CatalogItem } from './models/CatalogItem';
+export type { CatalogItemArtifact } from './models/CatalogItemArtifact';
+export { CatalogItemArtifactType } from './models/CatalogItemArtifactType';
+export { CatalogItemCategory } from './models/CatalogItemCategory';
+export type { CatalogItemConfigurable } from './models/CatalogItemConfigurable';
+export type { CatalogItemDeprecation } from './models/CatalogItemDeprecation';
+export type { CatalogItemList } from './models/CatalogItemList';
+export type { CatalogItemMeta } from './models/CatalogItemMeta';
+export type { CatalogItemReference } from './models/CatalogItemReference';
+export type { CatalogItemSpec } from './models/CatalogItemSpec';
+export { CatalogItemType } from './models/CatalogItemType';
+export type { CatalogItemVersion } from './models/CatalogItemVersion';
+export { CatalogItemVisibility } from './models/CatalogItemVisibility';
+export type { CatalogList } from './models/CatalogList';
+export type { CatalogSpec } from './models/CatalogSpec';
+export type { CatalogStatus } from './models/CatalogStatus';
+export type { Status } from './models/Status';
diff --git a/libs/types/alpha/models/ApiVersion.ts b/libs/types/alpha/models/ApiVersion.ts
new file mode 100644
index 000000000..d1e05a873
--- /dev/null
+++ b/libs/types/alpha/models/ApiVersion.ts
@@ -0,0 +1,11 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources.
+ */
+export enum ApiVersion {
+ V1ALPHA1 = 'v1alpha1',
+ FLIGHTCTL_IO_V1ALPHA1 = 'flightctl.io/v1alpha1',
+}
diff --git a/libs/types/alpha/models/Catalog.ts b/libs/types/alpha/models/Catalog.ts
new file mode 100644
index 000000000..e6326edd0
--- /dev/null
+++ b/libs/types/alpha/models/Catalog.ts
@@ -0,0 +1,19 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ApiVersion } from './ApiVersion';
+import type { CatalogSpec } from './CatalogSpec';
+import type { CatalogStatus } from './CatalogStatus';
+import type { ObjectMeta } from '../../models/ObjectMeta';
+export type Catalog = {
+ apiVersion: ApiVersion;
+ /**
+ * Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds.
+ */
+ kind: string;
+ metadata: ObjectMeta;
+ spec: CatalogSpec;
+ status?: CatalogStatus;
+};
+
diff --git a/libs/types/alpha/models/CatalogItem.ts b/libs/types/alpha/models/CatalogItem.ts
new file mode 100644
index 000000000..57ba61670
--- /dev/null
+++ b/libs/types/alpha/models/CatalogItem.ts
@@ -0,0 +1,20 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ApiVersion } from './ApiVersion';
+import type { CatalogItemMeta } from './CatalogItemMeta';
+import type { CatalogItemSpec } from './CatalogItemSpec';
+/**
+ * CatalogItem represents an application template from a catalog. It provides default configuration values that can be customized when adding the application to a fleet.
+ */
+export type CatalogItem = {
+ apiVersion: ApiVersion;
+ /**
+ * Kind is a string value representing the REST resource this object represents.
+ */
+ kind: string;
+ metadata: CatalogItemMeta;
+ spec: CatalogItemSpec;
+};
+
diff --git a/libs/types/alpha/models/CatalogItemArtifact.ts b/libs/types/alpha/models/CatalogItemArtifact.ts
new file mode 100644
index 000000000..dcce127c0
--- /dev/null
+++ b/libs/types/alpha/models/CatalogItemArtifact.ts
@@ -0,0 +1,20 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { CatalogItemArtifactType } from './CatalogItemArtifactType';
+/**
+ * An alternative artifact format.
+ */
+export type CatalogItemArtifact = {
+ type?: CatalogItemArtifactType;
+ /**
+ * Optional human-readable display name for this artifact.
+ */
+ name?: string;
+ /**
+ * Artifact URI (OCI reference, URL, S3 path, etc.).
+ */
+ uri: string;
+};
+
diff --git a/libs/types/alpha/models/CatalogItemArtifactType.ts b/libs/types/alpha/models/CatalogItemArtifactType.ts
new file mode 100644
index 000000000..869498ca9
--- /dev/null
+++ b/libs/types/alpha/models/CatalogItemArtifactType.ts
@@ -0,0 +1,18 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Type of artifact format. Includes bootc-image-builder output formats. Defaults to container if only one artifact.
+ */
+export enum CatalogItemArtifactType {
+ CatalogItemArtifactTypeContainer = 'container',
+ CatalogItemArtifactTypeQcow2 = 'qcow2',
+ CatalogItemArtifactTypeAmi = 'ami',
+ CatalogItemArtifactTypeIso = 'iso',
+ CatalogItemArtifactTypeAnacondaIso = 'anaconda-iso',
+ CatalogItemArtifactTypeVmdk = 'vmdk',
+ CatalogItemArtifactTypeVhd = 'vhd',
+ CatalogItemArtifactTypeRaw = 'raw',
+ CatalogItemArtifactTypeGce = 'gce',
+}
diff --git a/libs/types/alpha/models/CatalogItemCategory.ts b/libs/types/alpha/models/CatalogItemCategory.ts
new file mode 100644
index 000000000..2b83bbefc
--- /dev/null
+++ b/libs/types/alpha/models/CatalogItemCategory.ts
@@ -0,0 +1,11 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Category of a catalog item.
+ */
+export enum CatalogItemCategory {
+ CatalogItemCategorySystem = 'system',
+ CatalogItemCategoryApplication = 'application',
+}
diff --git a/libs/types/alpha/models/CatalogItemConfigurable.ts b/libs/types/alpha/models/CatalogItemConfigurable.ts
new file mode 100644
index 000000000..27b4bf02c
--- /dev/null
+++ b/libs/types/alpha/models/CatalogItemConfigurable.ts
@@ -0,0 +1,22 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Configuration fields that can be specified at item level (as defaults) and overridden at version level. Version-level values fully replace item-level values (not merged).
+ */
+export type CatalogItemConfigurable = {
+ /**
+ * Configuration values (envVars, ports, volumes, resources, etc.).
+ */
+ config?: Record;
+ /**
+ * JSON Schema defining configurable parameters and their validation.
+ */
+ configSchema?: Record;
+ /**
+ * Detailed documentation, preferably in markdown format.
+ */
+ readme?: string;
+};
+
diff --git a/libs/types/alpha/models/CatalogItemDeprecation.ts b/libs/types/alpha/models/CatalogItemDeprecation.ts
new file mode 100644
index 000000000..91603de32
--- /dev/null
+++ b/libs/types/alpha/models/CatalogItemDeprecation.ts
@@ -0,0 +1,18 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Deprecation information for a catalog item or version. Presence indicates deprecated status.
+ */
+export type CatalogItemDeprecation = {
+ /**
+ * Required message explaining why this is deprecated and what to do instead.
+ */
+ message: string;
+ /**
+ * Optional name of the replacement catalog item (item-level only).
+ */
+ replacement?: string;
+};
+
diff --git a/libs/types/alpha/models/CatalogItemList.ts b/libs/types/alpha/models/CatalogItemList.ts
new file mode 100644
index 000000000..a958abdb9
--- /dev/null
+++ b/libs/types/alpha/models/CatalogItemList.ts
@@ -0,0 +1,23 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ApiVersion } from './ApiVersion';
+import type { CatalogItem } from './CatalogItem';
+import type { ListMeta } from '../../models/ListMeta';
+/**
+ * CatalogItemList is a list of CatalogItems.
+ */
+export type CatalogItemList = {
+ apiVersion: ApiVersion;
+ /**
+ * Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds.
+ */
+ kind: string;
+ metadata: ListMeta;
+ /**
+ * List of CatalogItems.
+ */
+ items: Array;
+};
+
diff --git a/libs/types/alpha/models/CatalogItemMeta.ts b/libs/types/alpha/models/CatalogItemMeta.ts
new file mode 100644
index 000000000..264837f46
--- /dev/null
+++ b/libs/types/alpha/models/CatalogItemMeta.ts
@@ -0,0 +1,15 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ObjectMeta } from '../../models/ObjectMeta';
+/**
+ * Metadata for CatalogItem resources. Extends ObjectMeta with catalog scoping.
+ */
+export type CatalogItemMeta = (ObjectMeta & {
+ /**
+ * The catalog this item belongs to. Similar to namespace in Kubernetes.
+ */
+ catalog: string;
+});
+
diff --git a/libs/types/alpha/models/CatalogItemReference.ts b/libs/types/alpha/models/CatalogItemReference.ts
new file mode 100644
index 000000000..63f7f8de0
--- /dev/null
+++ b/libs/types/alpha/models/CatalogItemReference.ts
@@ -0,0 +1,19 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { CatalogItemArtifact } from './CatalogItemArtifact';
+/**
+ * Reference to the primary artifact and optional alternative formats.
+ */
+export type CatalogItemReference = {
+ /**
+ * Primary artifact URI without version tag. Supports OCI references, URLs, S3 paths, etc.
+ */
+ uri: string;
+ /**
+ * Alternative artifact formats (e.g., qcow2, ISO for bootc images).
+ */
+ artifacts?: Array;
+};
+
diff --git a/libs/types/alpha/models/CatalogItemSpec.ts b/libs/types/alpha/models/CatalogItemSpec.ts
new file mode 100644
index 000000000..b4f92de58
--- /dev/null
+++ b/libs/types/alpha/models/CatalogItemSpec.ts
@@ -0,0 +1,55 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { CatalogItemCategory } from './CatalogItemCategory';
+import type { CatalogItemConfigurable } from './CatalogItemConfigurable';
+import type { CatalogItemDeprecation } from './CatalogItemDeprecation';
+import type { CatalogItemReference } from './CatalogItemReference';
+import type { CatalogItemType } from './CatalogItemType';
+import type { CatalogItemVersion } from './CatalogItemVersion';
+import type { CatalogItemVisibility } from './CatalogItemVisibility';
+/**
+ * CatalogItemSpec defines the configuration for a catalog item.
+ */
+export type CatalogItemSpec = {
+ category?: CatalogItemCategory;
+ type: CatalogItemType;
+ reference: CatalogItemReference;
+ /**
+ * Available versions using Cincinnati model. Use replaces for primary edge, skips when stable channel skips intermediate versions.
+ */
+ versions: Array;
+ defaults?: CatalogItemConfigurable;
+ /**
+ * Human-readable display name shown in catalog listings.
+ */
+ displayName?: string;
+ /**
+ * A brief one-line description of the catalog item.
+ */
+ shortDescription?: string;
+ /**
+ * URL or data URI of the catalog item icon for display in UI.
+ */
+ icon?: string;
+ visibility?: CatalogItemVisibility;
+ deprecation?: CatalogItemDeprecation;
+ /**
+ * Provider or publisher of the catalog item (company or team name).
+ */
+ provider?: string;
+ /**
+ * Link to support resources or documentation for getting help.
+ */
+ support?: string;
+ /**
+ * The homepage URL for the catalog item project.
+ */
+ homepage?: string;
+ /**
+ * Link to external documentation.
+ */
+ documentationUrl?: string;
+};
+
diff --git a/libs/types/alpha/models/CatalogItemType.ts b/libs/types/alpha/models/CatalogItemType.ts
new file mode 100644
index 000000000..49575fe05
--- /dev/null
+++ b/libs/types/alpha/models/CatalogItemType.ts
@@ -0,0 +1,17 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Type of catalog item within its category.
+ */
+export enum CatalogItemType {
+ CatalogItemTypeOS = 'os',
+ CatalogItemTypeFirmware = 'firmware',
+ CatalogItemTypeDriver = 'driver',
+ CatalogItemTypeContainer = 'container',
+ CatalogItemTypeHelm = 'helm',
+ CatalogItemTypeQuadlet = 'quadlet',
+ CatalogItemTypeCompose = 'compose',
+ CatalogItemTypeData = 'data',
+}
diff --git a/libs/types/alpha/models/CatalogItemVersion.ts b/libs/types/alpha/models/CatalogItemVersion.ts
new file mode 100644
index 000000000..6c54a70cd
--- /dev/null
+++ b/libs/types/alpha/models/CatalogItemVersion.ts
@@ -0,0 +1,46 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { CatalogItemConfigurable } from './CatalogItemConfigurable';
+import type { CatalogItemDeprecation } from './CatalogItemDeprecation';
+/**
+ * A version of a catalog item following the Cincinnati model where versions
+ * are nodes in an upgrade graph and channels are labels on those nodes.
+ * Upgrade edges are defined by replaces (single), skips (multiple), or
+ * skipRange (semver range). Includes CatalogItemConfigurable fields that
+ * override item-level defaults. Exactly one of tag or digest must be specified.
+ *
+ */
+export type CatalogItemVersion = (CatalogItemConfigurable & {
+ /**
+ * Semantic version identifier (e.g., 1.2.3, v2.0.0-rc1). Required for version ordering and upgrade graph.
+ */
+ version: string;
+ /**
+ * Image tag to pull. Mutually exclusive with digest.
+ */
+ tag?: string;
+ /**
+ * OCI digest for immutable reference. Mutually exclusive with tag. Format: sha256:...
+ */
+ digest?: string;
+ /**
+ * Channels this version belongs to.
+ */
+ channels: Array;
+ /**
+ * The single version this one replaces, defining the primary upgrade edge.
+ */
+ replaces?: string;
+ /**
+ * Additional versions that can upgrade directly to this one. Use when stable channel skips intermediate fast-only versions.
+ */
+ skips?: Array;
+ /**
+ * Semver range of versions that can upgrade directly to this one. Use for z-stream updates or hotfixes.
+ */
+ skipRange?: string;
+ deprecation?: CatalogItemDeprecation;
+});
+
diff --git a/libs/types/alpha/models/CatalogItemVisibility.ts b/libs/types/alpha/models/CatalogItemVisibility.ts
new file mode 100644
index 000000000..578b43dc1
--- /dev/null
+++ b/libs/types/alpha/models/CatalogItemVisibility.ts
@@ -0,0 +1,11 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Visibility controls who can see and use the catalog item.
+ */
+export enum CatalogItemVisibility {
+ CatalogItemVisibilityDraft = 'draft',
+ CatalogItemVisibilityPublished = 'published',
+}
diff --git a/libs/types/alpha/models/CatalogList.ts b/libs/types/alpha/models/CatalogList.ts
new file mode 100644
index 000000000..74b0e57c7
--- /dev/null
+++ b/libs/types/alpha/models/CatalogList.ts
@@ -0,0 +1,23 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ApiVersion } from './ApiVersion';
+import type { Catalog } from './Catalog';
+import type { ListMeta } from '../../models/ListMeta';
+/**
+ * CatalogList is a list of Catalogs.
+ */
+export type CatalogList = {
+ apiVersion: ApiVersion;
+ /**
+ * Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds.
+ */
+ kind: string;
+ metadata: ListMeta;
+ /**
+ * List of Catalogs.
+ */
+ items: Array;
+};
+
diff --git a/libs/types/alpha/models/CatalogSpec.ts b/libs/types/alpha/models/CatalogSpec.ts
new file mode 100644
index 000000000..fc319c6b9
--- /dev/null
+++ b/libs/types/alpha/models/CatalogSpec.ts
@@ -0,0 +1,32 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { CatalogItemVisibility } from './CatalogItemVisibility';
+/**
+ * CatalogSpec describes the configuration of a catalog. Catalogs are containers for locally-managed CatalogItems.
+ */
+export type CatalogSpec = {
+ /**
+ * Human-readable display name shown in catalog listings.
+ */
+ displayName?: string;
+ /**
+ * A brief one-line description of the catalog.
+ */
+ shortDescription?: string;
+ /**
+ * URL or data URI of the catalog icon for display in UI.
+ */
+ icon?: string;
+ visibility?: CatalogItemVisibility;
+ /**
+ * Provider or publisher of the catalog (company or team name).
+ */
+ provider?: string;
+ /**
+ * Link to support resources or documentation for getting help.
+ */
+ support?: string;
+};
+
diff --git a/libs/types/alpha/models/CatalogStatus.ts b/libs/types/alpha/models/CatalogStatus.ts
new file mode 100644
index 000000000..63b4d241f
--- /dev/null
+++ b/libs/types/alpha/models/CatalogStatus.ts
@@ -0,0 +1,15 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { Condition } from '../../models/Condition';
+/**
+ * CatalogStatus represents the current status of a catalog source.
+ */
+export type CatalogStatus = {
+ /**
+ * Current state of the catalog source.
+ */
+ conditions: Array;
+};
+
diff --git a/libs/types/alpha/models/Status.ts b/libs/types/alpha/models/Status.ts
new file mode 100644
index 000000000..2faa28ad8
--- /dev/null
+++ b/libs/types/alpha/models/Status.ts
@@ -0,0 +1,32 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ApiVersion } from './ApiVersion';
+/**
+ * Status is a return value for calls that don't return other objects.
+ */
+export type Status = {
+ apiVersion: ApiVersion;
+ /**
+ * Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds.
+ */
+ kind: string;
+ /**
+ * Suggested HTTP return code for this status, 0 if not set.
+ */
+ code: number;
+ /**
+ * A human-readable description of the status of this operation.
+ */
+ message: string;
+ /**
+ * A machine-readable description of why this operation is in the "Failure" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.
+ */
+ reason: string;
+ /**
+ * Status of the operation. One of: "Success" or "Failure". More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status.
+ */
+ status: string;
+};
+
diff --git a/libs/types/package.json b/libs/types/package.json
index 9151c9557..8e1eb25c2 100644
--- a/libs/types/package.json
+++ b/libs/types/package.json
@@ -16,6 +16,11 @@
"source": "./imagebuilder/index.ts",
"types": "./dist/imagebuilder/index.d.ts",
"default": "./dist/imagebuilder/index.js"
+ },
+ "./alpha": {
+ "source": "./alpha/index.ts",
+ "types": "./dist/alpha/index.d.ts",
+ "default": "./dist/alpha/index.js"
}
},
"files": [
diff --git a/libs/types/scripts/openapi-typescript.js b/libs/types/scripts/openapi-typescript.js
index d51c32461..ac68552af 100644
--- a/libs/types/scripts/openapi-typescript.js
+++ b/libs/types/scripts/openapi-typescript.js
@@ -5,14 +5,14 @@ const path = require('path');
const OpenAPI = require('openapi-typescript-codegen');
const YAML = require('js-yaml');
-const { rimraf, copyDir, fixImagebuilderCoreReferences } = require('./openapi-utils');
+const { rimraf, copyDir, fixCoreReferences } = require('./openapi-utils');
const CORE_API = 'core';
+const ALPHA_CORE_API = 'alphacore';
const IMAGEBUILDER_API = 'imagebuilder';
const getSwaggerUrl = (api) => {
- const apiVersion = api === CORE_API ? 'v1beta1' : 'v1alpha1';
- return `https://raw.githubusercontent.com/flightctl/flightctl/main/api/${api}/${apiVersion}/openapi.yaml`;
+ return `https://raw.githubusercontent.com/flightctl/flightctl/main/api/${api}/openapi.yaml`;
};
const processJsonAPI = (jsonString) => {
@@ -32,14 +32,23 @@ const processJsonAPI = (jsonString) => {
async function generateTypes(mode) {
const config = {
[CORE_API]: {
- swaggerUrl: getSwaggerUrl(CORE_API),
+ swaggerUrl: getSwaggerUrl('core/v1beta1'),
output: path.resolve(__dirname, '../tmp-types'),
finalDir: path.resolve(__dirname, '../models'),
},
+ [ALPHA_CORE_API]: {
+ swaggerUrl: getSwaggerUrl('core/v1alpha1'),
+ output: path.resolve(__dirname, '../tmp-alpha-types'),
+ finalDir: path.resolve(__dirname, '../alpha/models'),
+ coreRef: 'v1beta1_openapi_yaml_components_schemas',
+ outputDir: path.resolve(__dirname, '../alpha'),
+ },
[IMAGEBUILDER_API]: {
- swaggerUrl: getSwaggerUrl(IMAGEBUILDER_API),
+ swaggerUrl: getSwaggerUrl('imagebuilder/v1alpha1'),
output: path.resolve(__dirname, '../tmp-imagebuilder-types'),
finalDir: path.resolve(__dirname, '../imagebuilder/models'),
+ coreRef: 'core_v1beta1_openapi_yaml_components_schemas',
+ outputDir: path.resolve(__dirname, '../imagebuilder'),
},
};
@@ -70,23 +79,22 @@ async function generateTypes(mode) {
await copyDir(output, path.resolve(__dirname, '..'));
await rimraf(output);
} else {
- // Image builder types need to be fixed before they can be moved to their final location
+ // Image builder and alpha types need to be fixed before they can be moved to their final location
await rimraf(finalDir);
const modelsDir = path.join(output, 'models');
if (fs.existsSync(modelsDir)) {
await copyDir(modelsDir, finalDir);
}
console.log(`Fixing references to core API types...`);
- await fixImagebuilderCoreReferences(finalDir);
+ await fixCoreReferences(finalDir, config[mode].coreRef);
// Copy the generated index.ts to imagebuilder/index.ts
const indexPath = path.join(output, 'index.ts');
if (fs.existsSync(indexPath)) {
- const imagebuilderDir = path.resolve(__dirname, '../imagebuilder');
- if (!fs.existsSync(imagebuilderDir)) {
- fs.mkdirSync(imagebuilderDir, { recursive: true });
+ if (!fs.existsSync(config[mode].outputDir)) {
+ fs.mkdirSync(config[mode].outputDir, { recursive: true });
}
- await fsPromises.copyFile(indexPath, path.join(imagebuilderDir, 'index.ts'));
+ await fsPromises.copyFile(indexPath, path.join(config[mode].outputDir, 'index.ts'));
}
await rimraf(output);
}
@@ -107,6 +115,7 @@ async function main() {
console.log('Generating types...');
await generateTypes(CORE_API);
+ await generateTypes(ALPHA_CORE_API);
await generateTypes(IMAGEBUILDER_API);
console.log('✅ Type generation complete!');
diff --git a/libs/types/scripts/openapi-utils.js b/libs/types/scripts/openapi-utils.js
index 2de984859..f186b4157 100755
--- a/libs/types/scripts/openapi-utils.js
+++ b/libs/types/scripts/openapi-utils.js
@@ -66,42 +66,25 @@ function findTsFiles(dir) {
return files;
}
-/**
- * Fixes references from the auto-generated imagebuilder types so they point to the correct types of the "core" API module.
- * The generated types are in the form of:
- * import type { core_v1beta1_openapi_yaml_components_schemas_ObjectMeta } from './core_v1beta1_openapi_yaml_components_schemas_ObjectMeta';
- * type SomeType = {
- * ...
- * someField: core_v1beta1_openapi_yaml_components_schemas_ObjectMeta;
- * }
- *
- * The fixed types will be like this:
- * import type { ObjectMeta } from '../../models/ObjectMeta';
- * type SomeType = {
- * ...
- * someField: ObjectMeta;
- * }
- *
- * @param {string} modelsDir - Directory containing TypeScript files to fix
- */
-async function fixImagebuilderCoreReferences(modelsDir) {
+async function fixCoreReferences(modelsDir, prefix) {
const files = findTsFiles(modelsDir);
+ // Pre-construct the Regex patterns using the dynamic prefix
+ // We escape special characters in the prefix if needed, though usually not for schema names
+ const importRegex = new RegExp(`from\\s+['"]\\.\\/${prefix}_([A-Za-z][A-Za-z0-9]*)['"]`, 'g');
+ const typeRegex = new RegExp(`\\b${prefix}_([A-Za-z][A-Za-z0-9]*)\\b`, 'g');
+
await Promise.all(
files.map(async (filePath) => {
let content = await fsPromises.readFile(filePath, 'utf8');
const originalContent = content;
- // Modify the path to properly point to the type from the "core" module
- content = content.replace(
- /from\s+['"]\.\/core_v1beta1_openapi_yaml_components_schemas_([A-Za-z][A-Za-z0-9]*)['"]/g,
- "from '../../models/$1'",
- );
+ // 1. Modify the import path
+ content = content.replace(importRegex, "from '../../models/$1'");
- // Correct the import name and the references to this type by removing the prefix
- content = content.replace(/\bcore_v1beta1_openapi_yaml_components_schemas_([A-Za-z][A-Za-z0-9]*)\b/g, '$1');
+ // 2. Correct the name references by removing the prefix
+ content = content.replace(typeRegex, '$1');
- // Only write if content changed
if (content !== originalContent) {
await fsPromises.writeFile(filePath, content, 'utf8');
}
@@ -112,5 +95,5 @@ async function fixImagebuilderCoreReferences(modelsDir) {
module.exports = {
rimraf,
copyDir,
- fixImagebuilderCoreReferences,
+ fixCoreReferences,
};
diff --git a/libs/ui-components/assets/flight-control-logo.png b/libs/ui-components/assets/flight-control-logo.png
new file mode 100644
index 000000000..ed4451080
Binary files /dev/null and b/libs/ui-components/assets/flight-control-logo.png differ
diff --git a/libs/ui-components/package.json b/libs/ui-components/package.json
index ac9982178..b6dc0fa56 100644
--- a/libs/ui-components/package.json
+++ b/libs/ui-components/package.json
@@ -31,6 +31,9 @@
"react-router-dom": "6.30.3"
},
"dependencies": {
+ "@rjsf/core": "5.24.13",
+ "@rjsf/utils": "5.24.13",
+ "@rjsf/validator-ajv8": "5.24.13",
"@types/js-yaml": "^4.0.9",
"@xterm/addon-fit": "^0.10.0",
"@xterm/xterm": "^5.5.0",
@@ -39,6 +42,8 @@
"fuzzysearch": "^1.0.3",
"js-yaml": "^4.1.1",
"percent-round": "^2.3.1",
+ "react-markdown": "^8.0.7",
+ "semver": "^7.7.3",
"use-debounce": "^10.0.1",
"yup": "^1.3.3"
},
@@ -50,12 +55,13 @@
"@patternfly/react-icons": "^6.4.0",
"@patternfly/react-styles": "^6.4.0",
"@patternfly/react-table": "^6.4.0",
- "victory": "^37.3.6",
+ "@patternfly/react-topology": "^6.4.0",
"i18next": "21.8.14 - 23.x",
+ "monaco-editor": "^0.51.0",
"react": "17.0.1 - 18.x",
"react-dom": "17.0.1 - 18.x",
"react-i18next": "11.7.3 - 15.x",
"react-router-dom": "^5.3 || ^6.30.3",
- "monaco-editor": "^0.51.0"
+ "victory": "^37.3.6"
}
}
diff --git a/libs/ui-components/src/components/Catalog/CatalogItemCard.tsx b/libs/ui-components/src/components/Catalog/CatalogItemCard.tsx
new file mode 100644
index 000000000..9bb57a708
--- /dev/null
+++ b/libs/ui-components/src/components/Catalog/CatalogItemCard.tsx
@@ -0,0 +1,86 @@
+import {
+ Card,
+ CardBody,
+ CardHeader,
+ Content,
+ ContentVariants,
+ Label,
+ Split,
+ SplitItem,
+ Stack,
+ StackItem,
+ Title,
+} from '@patternfly/react-core';
+import * as React from 'react';
+import { CatalogItem, CatalogItemCategory } from '@flightctl/types/alpha';
+
+import { useTranslation } from '../../hooks/useTranslation';
+import { getCatalogItemBadge, getCatalogItemIcon } from './utils';
+
+export type CatalogItemCardProps = {
+ catalogItem: CatalogItem;
+ onSelect: VoidFunction;
+};
+
+const CatalogItemCard: React.FC = ({ catalogItem, onSelect }) => {
+ const { t } = useTranslation();
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {catalogItem.spec.displayName || catalogItem.metadata.name}
+
+ {catalogItem.spec.provider && (
+
+
+ {t('Provided by {{provider}}', { provider: catalogItem.spec.provider })}
+
+
+ )}
+
+
+ {catalogItem.spec.shortDescription && {catalogItem.spec.shortDescription}}
+ {catalogItem.spec.deprecation && (
+
+
+
+ )}
+
+
+
+ );
+};
+
+export default CatalogItemCard;
diff --git a/libs/ui-components/src/components/Catalog/CatalogItemDetails.css b/libs/ui-components/src/components/Catalog/CatalogItemDetails.css
new file mode 100644
index 000000000..93373739d
--- /dev/null
+++ b/libs/ui-components/src/components/Catalog/CatalogItemDetails.css
@@ -0,0 +1,4 @@
+.fctl-catalog-item-details {
+ overflow-wrap: break-word;
+ word-break: break-word;
+}
diff --git a/libs/ui-components/src/components/Catalog/CatalogItemDetails.tsx b/libs/ui-components/src/components/Catalog/CatalogItemDetails.tsx
new file mode 100644
index 000000000..34a173e57
--- /dev/null
+++ b/libs/ui-components/src/components/Catalog/CatalogItemDetails.tsx
@@ -0,0 +1,294 @@
+import { CatalogItem, CatalogItemType } from '@flightctl/types/alpha';
+import {
+ Alert,
+ Button,
+ Content,
+ ContentVariants,
+ DescriptionList,
+ DescriptionListDescription,
+ DescriptionListGroup,
+ DescriptionListTerm,
+ Divider,
+ Drawer,
+ DrawerActions,
+ DrawerCloseButton,
+ DrawerContent,
+ DrawerContentBody,
+ DrawerHead,
+ DrawerPanelBody,
+ DrawerPanelContent,
+ Grid,
+ GridItem,
+ Split,
+ SplitItem,
+ Stack,
+ StackItem,
+ Title,
+} from '@patternfly/react-core';
+import * as React from 'react';
+import { createPortal } from 'react-dom';
+import * as semver from 'semver';
+import ReactMarkdown from 'react-markdown';
+import { Formik, useFormikContext } from 'formik';
+
+import { useTranslation } from '../../hooks/useTranslation';
+import { InstallSpec, InstallSpecFormik } from './InstallWizard/steps/SpecificationsStep';
+import FlightCtlForm from '../form/FlightCtlForm';
+import { getCatalogItemIcon } from './utils';
+
+import './CatalogItemDetails.css';
+
+type CatalogItemDetailsPanelProps = {
+ item: CatalogItem;
+ onClose: VoidFunction;
+ canInstall: boolean;
+};
+
+type CatalogItemDetailsProps = CatalogItemDetailsPanelProps & {
+ onInstall: (installItem: { item: CatalogItem; channel: string; version: string }) => void;
+};
+
+const getPageContentTop = () => {
+ // Try multiple selectors to find the masthead
+ const masthead =
+ document.getElementById('stack-inline-masthead') || // Standalone masthead
+ document.getElementById('page-main-header'); // OCP Console masthead
+
+ const pageTop = document.getElementById('fctl-cmd-panel');
+
+ return masthead?.getBoundingClientRect()?.bottom || pageTop?.getBoundingClientRect()?.top || 60;
+};
+
+const usePageContentTop = () => {
+ const [topOffset, setTopOffset] = React.useState(() => getPageContentTop());
+
+ React.useEffect(() => {
+ const measureTop = () => {
+ setTopOffset(getPageContentTop());
+ };
+
+ // Measure immediately
+ measureTop();
+
+ // Also measure after a short delay in case layout isn't complete
+ const timeoutId = setTimeout(measureTop, 50);
+
+ window.addEventListener('resize', measureTop);
+
+ return () => {
+ clearTimeout(timeoutId);
+ window.removeEventListener('resize', measureTop);
+ };
+ }, []);
+
+ return topOffset;
+};
+
+type CatalogItemDetailsHeaderProps = {
+ item: CatalogItem;
+};
+
+export const CatalogItemDetailsHeader = ({ item }: CatalogItemDetailsHeaderProps) => {
+ const { t } = useTranslation();
+ return (
+
+
+
+
+
+ {item.spec.displayName || item.metadata.name}
+ {item.spec.provider && (
+
+ {t('Provided by {{provider}}', { provider: item.spec.provider })}
+
+ )}
+
+
+ );
+};
+
+const CatalogItemDetailsPanel = ({ item, onClose, canInstall }: CatalogItemDetailsPanelProps) => {
+ const { t } = useTranslation();
+ const topOffset = usePageContentTop();
+
+ const {
+ values: { version, channel },
+ submitForm,
+ isValid,
+ } = useFormikContext();
+
+ const installEnabled = !!version && !!channel && canInstall;
+
+ const panelContent = (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {item.spec.type === CatalogItemType.CatalogItemTypeData ? (
+
+ ) : (
+
+
+
+ )}
+
+
+
+
+
+
+
+
+
+ );
+
+ const drawerWrapper = (
+