Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 4 additions & 2 deletions console/integration-tests/tests/install-pattern-page.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,10 @@ describe('Install Pattern Page', () => {

it('has Install and Cancel buttons', () => {
navigateToInstallPage();
cy.contains('button', 'Install').scrollIntoView().should('be.visible');
cy.contains('button', 'Cancel').scrollIntoView().should('be.visible');
cy.contains('button', 'Install').scrollIntoView();
cy.contains('button', 'Install').should('be.visible');
cy.contains('button', 'Cancel').scrollIntoView();
cy.contains('button', 'Cancel').should('be.visible');
});

it('Cancel button returns to the catalog', () => {
Expand Down
10 changes: 5 additions & 5 deletions console/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"build-dev": "yarn clean && yarn webpack",
"start": "concurrently --kill-others -c auto -n \"console,wds,ui-catalog\" -p \"{name}:{pid}\" \"yarn run start:console\" \"yarn run start:wds\" \"yarn run start:ui-catalog\"",
"start:wds": "PATTERN_UI_CATALOG_BASE_URL=http://localhost:8080 yarn run webpack -- serve --progress",
"start:console": "CONSOLE_VERSION=4.20.0 scripts/console.sh",
"start:console": "CONSOLE_VERSION=4.19.0 scripts/console.sh",
"start:ui-catalog": "scripts/catalog.sh",
"i18n": "./i18n-scripts/build-i18n.sh && node ./i18n-scripts/set-english-defaults.js",
"lint": "yarn eslint src integration-tests --fix && stylelint 'src/**/*.css' --allow-empty-input --fix",
Expand Down Expand Up @@ -61,15 +61,15 @@
"react-dom": "^17.0.1",
"react-helmet": "^6.1.0",
"react-i18next": "^11.7.3",
"react-router": "5.3.x",
"react-router-dom": "5.3.x",
"react-router": "~5.3.4",
"react-router-dom": "~5.3.4",
"style-loader": "^3.3.1",
"stylelint": "^15.3.0",
"stylelint-config-standard": "^31.0.0",
"ts-loader": "^9.3.1",
"ts-node": "^10.8.1",
"typescript": "^5.7.2",
"webpack": "^5.75.0",
"typescript": "^5.9.3",
"webpack": "^5.107.2",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.2.1"
},
Expand Down
63 changes: 42 additions & 21 deletions console/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export async function fetchCatalogImage(): Promise<string> {
const data = await response.json();
const containers = data.spec?.template?.spec?.containers || [];
const catalogContainer = containers.find(
(c: any) => c.name === 'patterns-operator-pattern-ui-catalog',
(c: { name?: string }) => c.name === 'patterns-operator-pattern-ui-catalog',
);
return catalogContainer?.image || 'unknown';
} catch (error) {
Expand Down Expand Up @@ -77,11 +77,19 @@ export async function fetchAllPatterns(): Promise<{
};
}

export interface ResourceCondition {
type?: string;
status?: string;
reason?: string;
message?: string;
lastTransitionTime?: string;
}

export interface VaultJobStatus {
jobName?: string;
status: 'not-found' | 'pending' | 'running' | 'succeeded' | 'failed';
message: string;
conditions?: any[];
conditions?: ResourceCondition[];
}

export interface VaultInjectionRequest {
Expand Down Expand Up @@ -426,15 +434,16 @@ PLAYBOOK_EOF
secretName,
};
} catch (error) {
const err = error as { name?: string; message?: string; stack?: string };
console.error('🔴 [API] Error triggering vault injection:', error);
console.error('🔴 [API] Error details:', {
name: error.name,
message: error.message,
stack: error.stack,
name: err.name,
message: err.message,
stack: err.stack,
});
return {
success: false,
message: `Error triggering vault injection: ${error.message || error}`,
message: `Error triggering vault injection: ${err.message || error}`,
};
}
}
Expand All @@ -457,11 +466,16 @@ export async function fetchVaultJobStatus(patternName: string): Promise<VaultJob
console.log(`📋 [API] Jobs response received:`, {
itemCount: data.items?.length || 0,
items:
data.items?.map((job) => ({
name: job.metadata?.name,
creationTimestamp: job.metadata?.creationTimestamp,
status: job.status,
})) || [],
data.items?.map(
(job: {
metadata?: { name?: string; creationTimestamp?: string };
status?: unknown;
}) => ({
name: job.metadata?.name,
creationTimestamp: job.metadata?.creationTimestamp,
status: job.status,
}),
) || [],
});

if (!data.items || data.items.length === 0) {
Expand All @@ -481,8 +495,11 @@ export async function fetchVaultJobStatus(patternName: string): Promise<VaultJob
creationTimestamp: job.metadata?.creationTimestamp,
status: jobStatus,
conditions:
jobStatus.conditions?.map((c) => ({ type: c.type, status: c.status, reason: c.reason })) ||
[],
jobStatus.conditions?.map((c: ResourceCondition) => ({
type: c.type,
status: c.status,
reason: c.reason,
})) || [],
});

let status: VaultJobStatus['status'] = 'pending';
Expand Down Expand Up @@ -514,15 +531,16 @@ export async function fetchVaultJobStatus(patternName: string): Promise<VaultJob
console.log(`📋 [API] Final job status result:`, result);
return result;
} catch (error) {
const err = error as { name?: string; message?: string; stack?: string };
console.error(`🔴 [API] Error fetching vault job status for pattern ${patternName}:`, error);
console.error(`🔴 [API] Error details:`, {
name: error.name,
message: error.message,
stack: error.stack,
name: err.name,
message: err.message,
stack: err.stack,
});
return {
status: 'not-found',
message: `Error checking vault job status: ${error.message || error}`,
message: `Error checking vault job status: ${err.message || error}`,
};
}
}
Expand All @@ -535,7 +553,9 @@ export async function fetchInstalledPatterns(): Promise<string[]> {
throw new Error(`Failed to fetch installed patterns: ${response.status}`);
}
const data = await response.json();
return (data.items || []).map((item: any) => item.metadata.name as string);
return (data.items || []).map(
(item: { metadata: { name: string } }) => item.metadata.name as string,
);
}

export interface PatternApplicationInfo {
Expand All @@ -551,7 +571,7 @@ export interface PatternCRStatus {
lastStep?: string;
lastError?: string;
deletionPhase?: string;
conditions?: any[];
conditions?: ResourceCondition[];
applications?: PatternApplicationInfo[];
version?: number;
}
Expand All @@ -578,16 +598,17 @@ export async function fetchPatternCR(name: string): Promise<PatternCRStatus> {
applications: status.applications,
version: status.version,
};
} catch (err) {
} catch (error) {
// consoleFetch may throw on 404 instead of returning a response
const err = error as { response?: { status?: number }; status?: number; message?: string };
if (
err?.response?.status === 404 ||
err?.status === 404 ||
(err?.message && /404|not found/i.test(err.message))
) {
return { exists: false };
}
throw err;
throw error;
}
}

Expand Down
5 changes: 3 additions & 2 deletions console/src/components/InstallPatternPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ export default function InstallPatternPage() {
}
} catch (err) {
console.error('🔴 [InstallPatternPage] Error triggering vault injection:', err);
const errorMessage = err.message || err.toString();
const errorMessage = err instanceof Error ? err.message : String(err);
setVaultJobStatus({
status: 'not-found',
message: `Failed to trigger vault injection: ${errorMessage}`,
Expand Down Expand Up @@ -347,7 +347,8 @@ export default function InstallPatternPage() {
}
} catch (err) {
console.error('🔴 [InstallPatternPage] Pattern installation failed:', err);
setSubmitError(err?.message || String(err));
const errorMessage = err instanceof Error ? err.message : String(err);
setSubmitError(errorMessage);
} finally {
setSubmitting(false);
console.log('🏁 [InstallPatternPage] Pattern installation process finished');
Expand Down
3 changes: 2 additions & 1 deletion console/src/components/ManageSecretsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ export default function ManageSecretsPage() {
setSubmitError(result.message);
}
} catch (err) {
setSubmitError(err?.message || String(err));
const errorMessage = err instanceof Error ? err.message : String(err);
setSubmitError(errorMessage);
} finally {
setSubmitting(false);
}
Expand Down
12 changes: 5 additions & 7 deletions console/src/components/PatternCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ function RequirementsPopoverBody({ pattern, clouds }: { pattern: Pattern; clouds
{clouds.map((cloud) => (
<StackItem key={cloud}>
<Label color="blue" isCompact>
{CLOUD_LABELS[cloud] || cloud}
{CLOUD_LABELS[cloud as CloudLabelKey] || cloud}
</Label>
<Table variant="compact" borders={false}>
<Thead>
Expand Down Expand Up @@ -164,9 +164,7 @@ export default function PatternCard({
const clouds = getCloudProviders(pattern);

return (
<Card
className={isDisabled ? 'patterns-operator__card--disabled' : ''}
>
<Card className={isDisabled ? 'patterns-operator__card--disabled' : ''}>
<CardHeader>
<Flex
justifyContent={{ default: 'justifyContentSpaceBetween' }}
Expand Down Expand Up @@ -215,8 +213,8 @@ export default function PatternCard({
aria-label={t('Tested requirements details')}
maxWidth="500px"
isVisible={isVisible}
shouldOpen={(_event, _fn) => setIsVisible(true)}
shouldClose={(_event, _fn) => setIsVisible(false)}
shouldOpen={() => setIsVisible(true)}
shouldClose={() => setIsVisible(false)}
headerContent={t('Tested requirements')}
bodyContent={<RequirementsPopoverBody pattern={pattern} clouds={clouds} />}
>
Expand All @@ -235,7 +233,7 @@ export default function PatternCard({
<LabelGroup>
{clouds.map((cloud) => (
<Label key={cloud} color="blue" isCompact>
{CLOUD_LABELS[cloud] || cloud}
{CLOUD_LABELS[cloud as CloudLabelKey] || cloud}
</Label>
))}
</LabelGroup>
Expand Down
6 changes: 4 additions & 2 deletions console/src/components/UninstallPatternPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ export default function UninstallPatternPage() {
}
} catch (err) {
// If the fetch fails with a 404-like error, treat as deleted
if (err?.message && /404|not found/i.test(err.message)) {
const errorMessage = err instanceof Error ? err.message : String(err);
if (errorMessage && /404|not found/i.test(errorMessage)) {
setDeleted(true);
setDeleting(false);
clearInterval(interval);
Expand All @@ -95,7 +96,8 @@ export default function UninstallPatternPage() {
setConfirmed(true);
}
} catch (err) {
setError(err?.message || String(err));
const message = err instanceof Error ? err.message : String(err);
setError(message);
setDeleting(false);
}
};
Expand Down
2 changes: 2 additions & 0 deletions console/src/declarations.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Ambient module declarations for non-code imports handled by webpack loaders.
declare module '*.css';
8 changes: 3 additions & 5 deletions console/src/hooks/useLocalStorage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useState } from "react";
import { useCallback, useState } from 'react';

interface StoredValue<T> {
version: number;
Expand Down Expand Up @@ -33,9 +33,7 @@ export default function useLocalStorage<T>(
initialValue: T,
version = 1,
): [T, (value: T | ((prev: T) => T)) => void] {
const [storedValue, setStoredValue] = useState<T>(() =>
readValue(key, initialValue, version),
);
const [storedValue, setStoredValue] = useState<T>(() => readValue(key, initialValue, version));

const setValue = useCallback(
(value: T | ((prev: T) => T)) => {
Expand All @@ -49,4 +47,4 @@ export default function useLocalStorage<T>(
);

return [storedValue, setValue];
}
}
12 changes: 9 additions & 3 deletions console/src/hooks/useNavigateCompat.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import * as React from 'react';
import * as ReactRouterDom from 'react-router-dom';

const hasUseNavigate = typeof (ReactRouterDom as any).useNavigate === 'function';
type RouterCompat = {
useNavigate: () => (path: string) => void;
useHistory: () => { push: (path: string) => void };
};

const router = ReactRouterDom as unknown as RouterCompat;
const hasUseNavigate = typeof router.useNavigate === 'function';

// React Router v6 removed useHistory in favor of useNavigate.
// OCP 4.22+ ships v6; OCP 4.21 and earlier ship v5.
export const useNavigateCompat: () => (path: string) => void = hasUseNavigate
? () => {
const navigate = (ReactRouterDom as any).useNavigate();
const navigate = router.useNavigate();
return React.useCallback((path: string) => navigate(path), [navigate]);
}
: () => {
const history = (ReactRouterDom as any).useHistory();
const history = router.useHistory();
return React.useCallback((path: string) => history.push(path), [history]);
};
15 changes: 11 additions & 4 deletions console/src/hooks/useParamsCompat.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import * as ReactRouterDom from 'react-router-dom';

const isV6 = typeof (ReactRouterDom as any).useNavigate === 'function';
type RouterCompat = {
useNavigate: () => unknown;
useParams: () => Record<string, string>;
useRouteMatch: (pattern: string) => { params?: Record<string, string> } | null;
};

const router = ReactRouterDom as unknown as RouterCompat;
const hasUseNavigate = typeof router.useNavigate === 'function';

// React Router v5 (OCP < 4.22): useParams may not work if the console
// framework doesn't expose route params through the standard context.
// useRouteMatch explicitly matches the current URL against the given pattern.
// React Router v6 (OCP 4.22+): useParams is the standard API.
export const useParamsCompat: (pattern: string) => Record<string, string> = isV6
? () => (ReactRouterDom as any).useParams()
export const useParamsCompat: (pattern: string) => Record<string, string> = hasUseNavigate
? () => router.useParams()
: (pattern: string) => {
const match = (ReactRouterDom as any).useRouteMatch(pattern);
const match = router.useRouteMatch(pattern);
return match?.params || {};
};
2 changes: 1 addition & 1 deletion console/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"sourceMap": true,
"jsx": "react",
"allowJs": true,
"strict": false,
"strict": true,
"noUnusedLocals": true,
"skipLibCheck": true
},
Expand Down
Loading
Loading