From 8b24b5b1834f32330ee2654acee46c147a08b3db Mon Sep 17 00:00:00 2001 From: Debsmita Santra Date: Wed, 21 Jan 2026 11:02:51 +0530 Subject: [PATCH] fix(extensions): show error when toggle fails --- .../.changeset/popular-pumpkins-punch.md | 5 ++ .../ExtensionsPackageEditContent.tsx | 14 +++++ .../InstalledPackagesTable.tsx | 37 +++++++++++++ .../InstalledPackages/RowActions.tsx | 54 ++++++++++++++----- .../pages/ExtensionsPackageInstallPage.tsx | 2 +- 5 files changed, 99 insertions(+), 13 deletions(-) create mode 100644 workspaces/extensions/.changeset/popular-pumpkins-punch.md diff --git a/workspaces/extensions/.changeset/popular-pumpkins-punch.md b/workspaces/extensions/.changeset/popular-pumpkins-punch.md new file mode 100644 index 0000000000..2dbbaaf340 --- /dev/null +++ b/workspaces/extensions/.changeset/popular-pumpkins-punch.md @@ -0,0 +1,5 @@ +--- +'@red-hat-developer-hub/backstage-plugin-extensions': patch +--- + +show error in the UI when toggle action fails diff --git a/workspaces/extensions/plugins/extensions/src/components/ExtensionsPackageEditContent.tsx b/workspaces/extensions/plugins/extensions/src/components/ExtensionsPackageEditContent.tsx index 658d024fc7..d5ef314506 100644 --- a/workspaces/extensions/plugins/extensions/src/components/ExtensionsPackageEditContent.tsx +++ b/workspaces/extensions/plugins/extensions/src/components/ExtensionsPackageEditContent.tsx @@ -55,6 +55,7 @@ import { TabPanel } from './TabPanel'; import { useInstallationContext } from './InstallationContext'; import { useTranslation } from '../hooks/useTranslation'; import { ExtensionsStatus, getPluginActionTooltipMessage } from '../utils'; +import { InstallationWarning } from './InstallationWarning'; interface TabItem { label: string; @@ -197,6 +198,13 @@ export const ExtensionsPackageEditContent = ({ const showRightCard = hasPackageExamples; + const showEditWarning = + (pkgConfig.data as any)?.error?.message && + (pkgConfig.data as any)?.error?.reason !== + ExtensionsStatus.INSTALLATION_DISABLED && + (pkgConfig.data as any)?.error?.reason !== + ExtensionsStatus.INSTALLATION_DISABLED_IN_PRODUCTION; + const handleSave = async () => { try { setSaveError(null); @@ -279,6 +287,12 @@ export const ExtensionsPackageEditContent = ({ return ( <> + {showEditWarning && } + {saveError && ( + + {saveError} + + )} {!pkg.spec?.dynamicArtifact && ( {t('alert.missingDynamicArtifactTitle')} diff --git a/workspaces/extensions/plugins/extensions/src/components/InstalledPackages/InstalledPackagesTable.tsx b/workspaces/extensions/plugins/extensions/src/components/InstalledPackages/InstalledPackagesTable.tsx index 9c24e31518..042df25c80 100644 --- a/workspaces/extensions/plugins/extensions/src/components/InstalledPackages/InstalledPackagesTable.tsx +++ b/workspaces/extensions/plugins/extensions/src/components/InstalledPackages/InstalledPackagesTable.tsx @@ -27,6 +27,8 @@ import { Query, QueryResult } from '@material-table/core'; import { useQuery } from '@tanstack/react-query'; import Box from '@mui/material/Box'; +import Snackbar from '@mui/material/Snackbar'; +import Alert from '@mui/material/Alert'; import { ExtensionsPackage, ExtensionsPackageInstallStatus, @@ -58,6 +60,8 @@ import { export const InstalledPackagesTable = () => { const { t } = useTranslation(); + const [rowActionError, setRowActionError] = useState(null); + const [snackbarOpen, setSnackbarOpen] = useState(false); const extensionsConfig = useExtensionsConfiguration(); const { installedPackages } = useInstallationContext(); const nodeEnvironment = useNodeEnvironment(); @@ -187,11 +191,19 @@ export const InstalledPackagesTable = () => { { + setRowActionError(err); + setSnackbarOpen(true); + }} /> { + setRowActionError(err); + setSnackbarOpen(true); + }} /> ); @@ -359,6 +371,31 @@ export const InstalledPackagesTable = () => { onClose={setOpenInstalledPackagesDialog} showPackages /> + { + setSnackbarOpen(false); + setRowActionError(null); + }} + anchorOrigin={{ vertical: 'top', horizontal: 'center' }} + sx={{ + top: '80px !important', + }} + > + { + setSnackbarOpen(false); + setRowActionError(null); + }} + severity="error" + sx={{ width: '100%' }} + > + {Array.isArray(rowActionError) + ? rowActionError.map((err, idx) =>
{err}
) + : rowActionError} +
+
); }; diff --git a/workspaces/extensions/plugins/extensions/src/components/InstalledPackages/RowActions.tsx b/workspaces/extensions/plugins/extensions/src/components/InstalledPackages/RowActions.tsx index 580273a902..09724d01a1 100644 --- a/workspaces/extensions/plugins/extensions/src/components/InstalledPackages/RowActions.tsx +++ b/workspaces/extensions/plugins/extensions/src/components/InstalledPackages/RowActions.tsx @@ -32,6 +32,7 @@ import { ExtensionsPackageInstallStatus } from '@red-hat-developer-hub/backstage import { useTranslation } from '../../hooks/useTranslation'; import { packageInstallRouteRef, packageRouteRef } from '../../routes'; import { usePackageConfig } from '../../hooks/usePackageConfig'; +import { usePackage } from '../../hooks/usePackage'; import { downloadPackageYAML } from '../../utils/downloadPackageYaml'; import { usePluginConfigurationPermissions } from '../../hooks/usePluginConfigurationPermissions'; import { useEnablePlugin } from '../../hooks/useEnablePlugin'; @@ -53,12 +54,15 @@ export type InstalledPackageRow = { export const DownloadPackageYaml = ({ pkg, isProductionEnv, + onError, }: { pkg: InstalledPackageRow; isProductionEnv: boolean; + onError?: (error: string) => void; }) => { const { t } = useTranslation(); const pkgConfig = usePackageConfig(pkg.namespace!, pkg.name!); + const packageEntity = usePackage(pkg.namespace!, pkg.name!); const packageConfigPermission = usePluginConfigurationPermissions( pkg.namespace!, pkg.parentPlugin ?? '', @@ -108,10 +112,31 @@ export const DownloadPackageYaml = ({ size="small" sx={{ color: theme => theme.palette.text.primary }} onClick={async () => { - await downloadPackageYAML( - pkgConfig.data?.configYaml ?? '', - pkg.name!, - ); + try { + const configYaml = pkgConfig.data?.configYaml ?? ''; + if (!configYaml) { + const dynamicArtifact = + packageEntity.data?.spec?.dynamicArtifact ?? + `./dynamic-plugins/dist/${pkg.packageName}`; + const minimalYaml = `plugins: + - package: ${JSON.stringify(dynamicArtifact)} + disabled: false +`; + // eslint-disable-next-line no-console + console.info( + `No configuration found for package ${pkg.name}, downloaded a minimal YAML`, + ); + await downloadPackageYAML(minimalYaml, pkg.name!); + return; + } + await downloadPackageYAML(configYaml, pkg.name!); + } catch (err: any) { + const errorMessage = + err?.message || err?.toString() || 'Unknown error occurred'; + onError?.( + `Failed to download package ${pkg.name}: ${errorMessage}`, + ); + } }} > @@ -235,10 +260,12 @@ export const TogglePackage = ({ pkg, isProductionEnv, isInstallationEnabled, + onError, }: { pkg: InstalledPackageRow; isProductionEnv: boolean; isInstallationEnabled: boolean; + onError?: (error: string) => void; }) => { const { t } = useTranslation(); const { installedPackages, setInstalledPackages } = useInstallationContext(); @@ -323,17 +350,20 @@ export const TogglePackage = ({ }; setInstalledPackages(updated); } else { - // eslint-disable-next-line no-console - console.warn( - `[Package Toggle] Package toggle responded with non-OK status:`, - (res as any)?.error?.message ?? res, + const errorMessage = + (res as any)?.error?.message ?? res?.toString() ?? 'Unknown error'; + onError?.( + `Failed to ${isPackageEnabled ? 'disable' : 'enable'} package ${pkg.name}: ${errorMessage}`, ); } } catch (err: any) { - // eslint-disable-next-line no-console - console.error( - `[Package Toggle] Failed to toggle package}:`, - err?.error?.message ?? err, + const errorMessage = + err?.error?.message ?? + err?.message ?? + err?.toString() ?? + 'Unknown error'; + onError?.( + `Failed to ${isPackageEnabled ? 'disable' : 'enable'} package ${pkg.name}: ${errorMessage}`, ); } }; diff --git a/workspaces/extensions/plugins/extensions/src/pages/ExtensionsPackageInstallPage.tsx b/workspaces/extensions/plugins/extensions/src/pages/ExtensionsPackageInstallPage.tsx index 54a977a376..ea55389e5e 100644 --- a/workspaces/extensions/plugins/extensions/src/pages/ExtensionsPackageInstallPage.tsx +++ b/workspaces/extensions/plugins/extensions/src/pages/ExtensionsPackageInstallPage.tsx @@ -35,7 +35,7 @@ const PackageEditHeader = () => { const pkg = usePackage(params.namespace, params.name); - const displayName = pkg.data?.metadata.title ?? params.name; + const displayName = pkg.data?.metadata?.title ?? params.name; const title = location?.state?.viewOnly || !pkg.data?.spec?.dynamicArtifact ? displayName