diff --git a/.cursor/rules/patternfly-ux-capitalization.mdc b/.cursor/rules/patternfly-ux-capitalization.mdc
new file mode 100644
index 0000000000..74e11167f9
--- /dev/null
+++ b/.cursor/rules/patternfly-ux-capitalization.mdc
@@ -0,0 +1,39 @@
+---
+description:
+ PatternFly UX writing — sentence case and capitalization for user-visible UI copy (aligned with PatternFly guidelines).
+globs:
+ - "**/*.tsx"
+ - "**/*.jsx"
+alwaysApply: false
+---
+
+# PatternFly capitalization (UI copy)
+
+Follow [PatternFly capitalization guidelines](https://www.patternfly.org/ux-writing/capitalization/) for **user-visible strings** (labels, headings, buttons, table text, empty states, errors, etc.).
+
+## Defaults
+
+- Use **sentence case**: capitalize only the **first word** of the phrase/sentence, plus **proper nouns**, **product names**, **acronyms**, and **initialisms** (e.g. OpenShift, OVN, API, DNS).
+- **Buttons, navigation, page titles, headings**: sentence case unless an existing product term requires otherwise.
+- Prefer **consistency** within the same surface (wizard step, table, modal).
+
+## Feature vs generic wording
+
+- Capitalize a name when it refers to a **specific product feature or UI location**; use **lowercase** when it is a **generic concept** (PatternFly’s *Compliance* / *Sources* examples apply the same idea).
+
+## Breadcrumbs and user-defined names
+
+- **Breadcrumbs**: match the capitalization of the **source page title** (even if mixed styles).
+- **User- or system-defined names** (e.g. cluster names, resource names): **preserve the exact casing** the user or API used.
+
+## Third-party products
+
+- Spell and capitalize **external product names** as in **that vendor’s** documentation.
+
+## Docs / component names in prose
+
+- In **documentation-style** text about components: component names **lowercase** unless they start a sentence. **Code identifiers** follow normal language conventions.
+
+## Scope note
+
+- **`libs/ui-lib/lib/ocm/**`** does not use `t('ai:…')`; still apply these rules to **plain English** UI strings there. **`t('ai:…')`** strings under `libs/ui-lib` (outside `ocm`) follow **`translations.mdc`** for workflow; English source strings should still respect sentence case where it fits the string catalog.
diff --git a/libs/locales/lib/en/translation.json b/libs/locales/lib/en/translation.json
index 512804d14c..e377804be8 100644
--- a/libs/locales/lib/en/translation.json
+++ b/libs/locales/lib/en/translation.json
@@ -184,7 +184,6 @@
"ai:Cluster CIDR": "Cluster CIDR",
"ai:Cluster configured and ready for installation.": "Cluster configured and ready for installation.",
"ai:Cluster details": "Cluster details",
- "ai:Cluster Details": "Cluster Details",
"ai:cluster doesn't contain the feature_usage field": "cluster does not contain the feature_usage field",
"ai:cluster doesn't contain the openshift_version field": "cluster does not contain the openshift_version field",
"ai:Cluster Events": "Cluster Events",
@@ -981,7 +980,7 @@
"ai:useAlerts must be used within AlertsContextProvider": "useAlerts must be used within AlertsContextProvider",
"ai:Used to describe hosts' physical location. Helps for quicker host selection during cluster creation.": "Used to describe hosts' physical location. Helps for quicker host selection during cluster creation.",
"ai:useFeatureSupportLevel must be used within FeatureSupportLevelContextProvider.": "useFeatureSupportLevel must be used within FeatureSupportLevelContextProvider.",
- "ai:User-Managed Networking": "User-Managed Networking",
+ "ai:User-Managed networking": "User-Managed networking",
"ai:Username": "Username",
"ai:UUID": "UUID",
"ai:Valid hostname": "Valid hostname",
@@ -1014,7 +1013,7 @@
"ai:With BMC form": "With BMC form",
"ai:With Discovery ISO": "With Discovery ISO",
"ai:With iPXE": "With iPXE",
- "ai:With User-Managed Networking, you'll need to provide and configure a load balancer for the API and ingress endpoints.": "With User-Managed Networking, you'll need to provide and configure a load balancer for the API and ingress endpoints.",
+ "ai:With User-Managed networking, you'll need to provide and configure a load balancer for the API and ingress endpoints.": "With User-Managed networking, you'll need to provide and configure a load balancer for the API and ingress endpoints.",
"ai:Worker_one": "Worker",
"ai:Worker_other": "Workers",
"ai:Worker Hosts Labels": "Worker Hosts Labels",
diff --git a/libs/ui-lib-tests/cypress/support/variables/networking.ts b/libs/ui-lib-tests/cypress/support/variables/networking.ts
index c75f301fff..74d5ad2ed0 100644
--- a/libs/ui-lib-tests/cypress/support/variables/networking.ts
+++ b/libs/ui-lib-tests/cypress/support/variables/networking.ts
@@ -17,7 +17,7 @@ Cypress.env(
'#form-input-clusterNetworks-0-hostPrefix-field-helper',
);
Cypress.env('serviceNetworkCidrFieldHelperId', '#form-input-serviceNetworks-0-cidr-field-helper');
-Cypress.env('userManagedNetworkingRadioText', 'User-Managed Networking');
+Cypress.env('userManagedNetworkingRadioText', 'User-Managed networking');
Cypress.env('openVirtualNetworkingRadioText', 'Open Virtual Networking');
Cypress.env('networkTypeToggleId', '#form-input-networkType-field');
Cypress.env('hostSubnetFieldId', '#form-input-hostSubnet-field');
diff --git a/libs/ui-lib/lib/common/components/clusterWizard/networkingSteps/ManagedNetworkingControlGroup.tsx b/libs/ui-lib/lib/common/components/clusterWizard/networkingSteps/ManagedNetworkingControlGroup.tsx
index 49aa7bd30a..3cb8c47487 100644
--- a/libs/ui-lib/lib/common/components/clusterWizard/networkingSteps/ManagedNetworkingControlGroup.tsx
+++ b/libs/ui-lib/lib/common/components/clusterWizard/networkingSteps/ManagedNetworkingControlGroup.tsx
@@ -55,10 +55,10 @@ export const ManagedNetworkingControlGroup = ({
value={'userManaged'}
label={
<>
- {t('ai:User-Managed Networking')}{' '}
+ {t('ai:User-Managed networking')}{' '}
>
diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/ReviewClusterDetailTable.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/ReviewClusterDetailTable.tsx
index 3f936b23ab..50d3a15c96 100644
--- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/ReviewClusterDetailTable.tsx
+++ b/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/ReviewClusterDetailTable.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import { Table, TableVariant, Tbody, Td, Tr } from '@patternfly/react-table';
import { genericTableRowKey, getDefaultCpuArchitecture } from '../../../../common';
-import { getDiskEncryptionEnabledOnStatus } from '../../clusterDetail/ClusterProperties';
+import { getDiskEncryptionEnabledOnStatus } from './utils';
import OpenShiftVersionDetail from '../../clusterDetail/OpenShiftVersionDetail';
import { useNewFeatureSupportLevel } from '../../../../common/components/newFeatureSupportLevels';
import { Cluster } from '@openshift-assisted/types/assisted-installer-service';
diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/ReviewNetworkingTable.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/ReviewNetworkingTable.tsx
index bfc0a56d44..8dfc804443 100644
--- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/ReviewNetworkingTable.tsx
+++ b/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/ReviewNetworkingTable.tsx
@@ -2,7 +2,7 @@ import { Title } from '@patternfly/react-core';
import { Table, TableVariant, Tbody, Td, Tr } from '@patternfly/react-table';
import React from 'react';
import { genericTableRowKey, isDualStack, NETWORK_TYPE_LABELS } from '../../../../common';
-import { getManagementType, getStackTypeLabel } from '../../clusterDetail/ClusterProperties';
+import { getManagementType, getStackTypeLabel } from './utils';
import { Cluster } from '@openshift-assisted/types/assisted-installer-service';
type ReviewTableRowsType = {
diff --git a/libs/ui-lib/lib/ocm/components/clusterDetail/ClusterProperties.test.ts b/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/utils.test.ts
similarity index 99%
rename from libs/ui-lib/lib/ocm/components/clusterDetail/ClusterProperties.test.ts
rename to libs/ui-lib/lib/ocm/components/clusterConfiguration/review/utils.test.ts
index dbba0e32ea..3cdd278ab6 100644
--- a/libs/ui-lib/lib/ocm/components/clusterDetail/ClusterProperties.test.ts
+++ b/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/utils.test.ts
@@ -1,5 +1,5 @@
import { test, describe, expect } from 'vitest';
-import { getStackTypeLabel } from './ClusterProperties';
+import { getStackTypeLabel } from './utils';
import { Cluster } from '@openshift-assisted/types/assisted-installer-service';
const createCluster = (overrides: Partial = {}): Cluster =>
diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/utils.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/utils.tsx
new file mode 100644
index 0000000000..1cc62d0236
--- /dev/null
+++ b/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/utils.tsx
@@ -0,0 +1,61 @@
+import React from 'react';
+import { isDualStack } from '../../../../common';
+import { Cluster, DiskEncryption } from '@openshift-assisted/types/assisted-installer-service';
+
+export const getManagementType = ({ userManagedNetworking }: Cluster): string =>
+ userManagedNetworking ? 'User-Managed networking' : 'Cluster-managed networking';
+
+export const getStackTypeLabel = (cluster: Cluster): string =>
+ isDualStack(cluster) ? 'Dual-stack' : 'IPv4';
+
+export const getDiskEncryptionEnabledOnStatus = (diskEncryption: DiskEncryption['enableOn']) => {
+ let diskEncryptionType = null;
+ switch (diskEncryption) {
+ case undefined:
+ case 'none':
+ break;
+ case 'all':
+ diskEncryptionType = (
+ <>
+ Enabled on control plane nodes
+
+ Enabled on workers
+ >
+ );
+ break;
+ case 'masters':
+ diskEncryptionType = <>Enabled on control plane nodes>;
+ break;
+ case 'workers':
+ diskEncryptionType = <>Enabled on workers>;
+ break;
+ case 'arbiters':
+ diskEncryptionType = <>Enabled on arbiters>;
+ break;
+ case 'masters,arbiters':
+ diskEncryptionType = <>Enabled on control plane nodes and arbiters>;
+ break;
+ case 'masters,workers':
+ diskEncryptionType = <>Enabled on control plane nodes and workers>;
+ break;
+ case 'arbiters,workers':
+ diskEncryptionType = <>Enabled on arbiters and workers>;
+ break;
+ case 'masters,arbiters,workers':
+ diskEncryptionType = (
+ <>
+ Enabled on control plane nodes
+
+ Enabled on arbiters
+
+ Enabled on workers
+ >
+ );
+ break;
+ default: {
+ const _exhaustive: never = diskEncryption;
+ return _exhaustive;
+ }
+ }
+ return diskEncryptionType;
+};
diff --git a/libs/ui-lib/lib/ocm/components/clusterDetail/AssistedInstallerExtraDetailCard.tsx b/libs/ui-lib/lib/ocm/components/clusterDetail/AssistedInstallerExtraDetailCard.tsx
deleted file mode 100644
index 981901570c..0000000000
--- a/libs/ui-lib/lib/ocm/components/clusterDetail/AssistedInstallerExtraDetailCard.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-import React from 'react';
-import { Provider } from 'react-redux';
-import { storeDay1 } from '../../store';
-import { useSelector } from 'react-redux';
-
-import { AlertsContextProvider, CpuArchitecture, FeatureListType } from '../../../common';
-import ClusterProperties from './ClusterProperties';
-import { Grid } from '@patternfly/react-core';
-import { NewFeatureSupportLevelProvider } from '../featureSupportLevels';
-import useInfraEnv from '../../hooks/useInfraEnv';
-import { usePullSecret } from '../../hooks';
-import { selectCurrentClusterState } from '../../store/slices/current-cluster/selectors';
-import { useFeatureDetection } from '../../hooks/use-feature-detection';
-import { OpenShiftVersionsContextProvider } from '../clusterWizard/OpenShiftVersionsContext';
-
-type AssistedInstallerExtraDetailCardProps = {
- allEnabledFeatures: FeatureListType;
-};
-
-const AssistedInstallerExtraDetailCard: React.FC = ({
- allEnabledFeatures,
-}) => {
- useFeatureDetection(allEnabledFeatures);
- const { data: cluster } = useSelector(selectCurrentClusterState);
- const pullSecret = usePullSecret();
- const { infraEnv } = useInfraEnv(
- cluster?.id ? cluster?.id : '',
- cluster?.cpuArchitecture
- ? (cluster.cpuArchitecture as CpuArchitecture)
- : CpuArchitecture.USE_DAY1_ARCHITECTURE,
- cluster?.name,
- pullSecret,
- cluster?.openshiftVersion,
- );
-
- if (!cluster || cluster.status === 'adding-hosts') {
- return null;
- }
-
- return (
-
- }
- cluster={cluster}
- cpuArchitecture={infraEnv?.cpuArchitecture as CpuArchitecture}
- openshiftVersion={cluster.openshiftVersion}
- platformType={cluster.platform?.type}
- >
-
-
-
-
-
- );
-};
-
-const Wrapper: React.FC = (props) => {
- return (
-
-
-
-
-
-
-
- );
-};
-
-export default Wrapper;
diff --git a/libs/ui-lib/lib/ocm/components/clusterDetail/ClusterProperties.tsx b/libs/ui-lib/lib/ocm/components/clusterDetail/ClusterProperties.tsx
deleted file mode 100644
index 78449bce7f..0000000000
--- a/libs/ui-lib/lib/ocm/components/clusterDetail/ClusterProperties.tsx
+++ /dev/null
@@ -1,169 +0,0 @@
-import React from 'react';
-import { GridItem, Content } from '@patternfly/react-core';
-import {
- DetailItem,
- DetailList,
- getDefaultCpuArchitecture,
- isDualStack,
- NETWORK_TYPE_LABELS,
- selectApiVip,
- selectIngressVip,
- selectIpv4Cidr,
- selectIpv4HostPrefix,
- selectIpv6Cidr,
- selectIpv6HostPrefix,
-} from '../../../common';
-import { useTranslation } from '../../../common/hooks/use-translation-wrapper';
-import { ClusterFeatureSupportLevelsDetailItem } from '../featureSupportLevels';
-import OpenShiftVersionDetail from './OpenShiftVersionDetail';
-import { useNewFeatureSupportLevel } from '../../../common/components/newFeatureSupportLevels';
-import { Cluster, DiskEncryption } from '@openshift-assisted/types/assisted-installer-service';
-
-type ClusterPropertiesProps = {
- cluster: Cluster;
- externalMode?: boolean;
-};
-
-export const getNetworkType = (clusterNetworkType: Cluster['networkType']): string => {
- return NETWORK_TYPE_LABELS[clusterNetworkType || ''];
-};
-
-export const getManagementType = ({ userManagedNetworking }: Cluster): string => {
- let managementType: string;
- userManagedNetworking
- ? (managementType = 'User-Managed Networking')
- : (managementType = 'Cluster-managed networking');
- return managementType;
-};
-
-export const getStackTypeLabel = (cluster: Cluster): string =>
- isDualStack(cluster) ? 'Dual-stack' : 'IPv4';
-
-export const getDiskEncryptionEnabledOnStatus = (diskEncryption: DiskEncryption['enableOn']) => {
- let diskEncryptionType = null;
- switch (diskEncryption) {
- case 'all':
- diskEncryptionType = (
- <>
- Enabled on control plane nodes
-
- Enabled on workers
- >
- );
- break;
- case 'masters':
- diskEncryptionType = <>Enabled on control plane nodes>;
- break;
- case 'workers':
- diskEncryptionType = <>Enabled on workers>;
- break;
- }
- return diskEncryptionType;
-};
-
-const ClusterProperties = ({ cluster, externalMode = false }: ClusterPropertiesProps) => {
- const { t } = useTranslation();
- const isDualStackType = isDualStack(cluster);
- const featureSupportLevelContext = useNewFeatureSupportLevel();
-
- const activeFeatureConfiguration = featureSupportLevelContext.activeFeatureConfiguration;
- const underlyingCpuArchitecture =
- activeFeatureConfiguration?.underlyingCpuArchitecture || getDefaultCpuArchitecture();
-
- return (
- <>
- {!externalMode && (
-
- {t('ai:Cluster Details')}
-
- )}
-
-
- {externalMode ? undefined : (
- }
- testId="openshift-version"
- />
- )}
-
-
-
-
-
-
-
-
-
-
-
-
- {isDualStackType ? (
- <>
-
-
- >
- ) : undefined}
-
- {isDualStackType ? (
-
- ) : undefined}
-
-
-
-
- >
- );
-};
-export default ClusterProperties;
diff --git a/libs/ui-lib/lib/ocm/components/clusterDetail/index.ts b/libs/ui-lib/lib/ocm/components/clusterDetail/index.ts
index f4d8de0216..0286b7b570 100644
--- a/libs/ui-lib/lib/ocm/components/clusterDetail/index.ts
+++ b/libs/ui-lib/lib/ocm/components/clusterDetail/index.ts
@@ -2,4 +2,3 @@ export * from './utils';
export { default as ClusterDetail } from './ClusterDetail';
export { default as ClusterInstallationProgressCard } from './ClusterInstallationProgressCard';
export { default as AssistedInstallerDetailCard } from './AssistedInstallerDetailCard';
-export { default as AssistedInstallerExtraDetailCard } from './AssistedInstallerExtraDetailCard';
diff --git a/libs/ui-lib/lib/ocm/components/featureSupportLevels/featureStateUtils.ts b/libs/ui-lib/lib/ocm/components/featureSupportLevels/featureStateUtils.ts
index 7bfd2398dc..29a38497a1 100644
--- a/libs/ui-lib/lib/ocm/components/featureSupportLevels/featureStateUtils.ts
+++ b/libs/ui-lib/lib/ocm/components/featureSupportLevels/featureStateUtils.ts
@@ -229,7 +229,7 @@ export const getNewFeatureDisabledReason = (
}
case 'USER_MANAGED_NETWORKING': {
if (!isSupported) {
- return `User-Managed Networking is not supported when using ${
+ return `User-Managed networking is not supported when using ${
platformType ? ExternalPlatformLabels[platformType] : ''
}`;
}