diff --git a/frontend/src/components/cluster/Overview.tsx b/frontend/src/components/cluster/Overview.tsx index 03ab012ebca..d05bd92f11a 100644 --- a/frontend/src/components/cluster/Overview.tsx +++ b/frontend/src/components/cluster/Overview.tsx @@ -8,7 +8,6 @@ import Node from '../../lib/k8s/node'; import Pod from '../../lib/k8s/pod'; import { useFilterFunc } from '../../lib/util'; import { DateLabel, Link, PageGrid, StatusLabel } from '../common'; -import Empty from '../common/EmptyContent'; import ResourceListView from '../common/Resource/ResourceListView'; import { SectionBox } from '../common/SectionBox'; import ShowHideLabel from '../common/ShowHideLabel'; @@ -19,6 +18,7 @@ import { NodesStatusCircleChart, PodsStatusCircleChart, } from './Charts'; +import { ClusterGroupErrorMessage } from './ClusterGroupErrorMessage'; export default function Overview() { const { t } = useTranslation(['translation']); @@ -35,7 +35,7 @@ export default function Overview() { {noPermissions ? ( - {t('translation|No permissions to list pods.')} + ) : ( @@ -74,7 +74,7 @@ function EventsSection() { ) ) ); - const [events, eventsError] = Event.useList({ limit: Event.maxLimit }); + const { items: events, errors: eventsErrors } = Event.useList({ limit: Event.maxLimit }); const warningActionFilterFunc = (event: Event, search?: string) => { if (!filterFunc(event, search)) { @@ -138,7 +138,7 @@ function EventsSection() { }} defaultGlobalFilter={eventsFilter ?? undefined} data={events} - errorMessage={Event.getErrorMessage(eventsError)} + errors={eventsErrors} columns={[ { label: t('Type'), diff --git a/frontend/src/components/common/Link.tsx b/frontend/src/components/common/Link.tsx index 69f1155a73c..cf4f55b6dd1 100644 --- a/frontend/src/components/common/Link.tsx +++ b/frontend/src/components/common/Link.tsx @@ -35,7 +35,7 @@ function KubeObjectLink(props: { kubeObject: KubeObject; [prop: string]: any }) const client = useQueryClient(); const { namespace, name } = kubeObject.metadata; - const endpoint = useEndpoints(kubeObject._class().apiEndpoint.apiInfo, kubeObject.cluster); + const { endpoint } = useEndpoints(kubeObject._class().apiEndpoint.apiInfo, kubeObject.cluster); return ( ); diff --git a/frontend/src/components/cronjob/Details.tsx b/frontend/src/components/cronjob/Details.tsx index f576f431270..cffccc6a5f6 100644 --- a/frontend/src/components/cronjob/Details.tsx +++ b/frontend/src/components/cronjob/Details.tsx @@ -124,7 +124,7 @@ export default function CronJobDetails(props: { name?: string; namespace?: strin const { t, i18n } = useTranslation('glossary'); const dispatch: AppDispatch = useDispatch(); - const [jobs, jobsError] = Job.useList({ namespace }); + const { items: jobs, errors } = Job.useList({ namespace }); const [cronJob] = CronJob.useGet(name, namespace); const [isSpawnDialogOpen, setIsSpawnDialogOpen] = useState(false); const [isPendingSuspend, setIsPendingSuspend] = useState(false); @@ -233,7 +233,7 @@ export default function CronJobDetails(props: { name?: string; namespace?: strin cronJob && [ , diff --git a/frontend/src/components/endpoints/__snapshots__/EndpointDetails.Error.stories.storyshot b/frontend/src/components/endpoints/__snapshots__/EndpointDetails.Error.stories.storyshot index a8ef8b13255..500740fe452 100644 --- a/frontend/src/components/endpoints/__snapshots__/EndpointDetails.Error.stories.storyshot +++ b/frontend/src/components/endpoints/__snapshots__/EndpointDetails.Error.stories.storyshot @@ -92,7 +92,7 @@

- Error: Unreachable + TypeError: Failed to fetch

diff --git a/frontend/src/components/horizontalPodAutoscaler/__snapshots__/HPADetails.Error.stories.storyshot b/frontend/src/components/horizontalPodAutoscaler/__snapshots__/HPADetails.Error.stories.storyshot index 45e720a92e9..e7d528c6071 100644 --- a/frontend/src/components/horizontalPodAutoscaler/__snapshots__/HPADetails.Error.stories.storyshot +++ b/frontend/src/components/horizontalPodAutoscaler/__snapshots__/HPADetails.Error.stories.storyshot @@ -79,7 +79,7 @@

- Error: Unreachable + TypeError: Failed to fetch

diff --git a/frontend/src/components/job/List.tsx b/frontend/src/components/job/List.tsx index 1b44d07faef..b8f4e5e09fa 100644 --- a/frontend/src/components/job/List.tsx +++ b/frontend/src/components/job/List.tsx @@ -1,6 +1,7 @@ import { Icon } from '@iconify/react'; import { Box } from '@mui/material'; import { useTranslation } from 'react-i18next'; +import { ApiError } from '../../lib/k8s/api/v2/ApiError'; import { KubeContainer } from '../../lib/k8s/cluster'; import Job from '../../lib/k8s/job'; import { formatDuration } from '../../lib/util'; @@ -54,20 +55,20 @@ export function makeJobStatusLabel(job: Job) { } export default function JobsList() { - const [jobs, error] = Job.useList({ namespace: useNamespaces() }); - return ; + const { items: jobs, errors } = Job.useList({ namespace: useNamespaces() }); + return ; } export interface JobsListRendererProps { jobs: Job[] | null; - error: string | null; + errors?: ApiError[] | null; hideColumns?: 'namespace'[]; reflectTableInURL?: SimpleTableProps['reflectInURL']; noNamespaceFilter?: boolean; } export function JobsListRenderer(props: JobsListRendererProps) { - const { jobs, error, hideColumns = [], reflectTableInURL = 'jobs', noNamespaceFilter } = props; + const { jobs, errors, hideColumns = [], reflectTableInURL = 'jobs', noNamespaceFilter } = props; const { t } = useTranslation(['glossary', 'translation']); function getCompletions(job: Job) { @@ -89,7 +90,7 @@ export function JobsListRenderer(props: JobsListRendererProps) { noNamespaceFilter, }} hideColumns={hideColumns} - errorMessage={error} + errors={errors} columns={[ 'name', 'namespace', diff --git a/frontend/src/components/limitRange/List.tsx b/frontend/src/components/limitRange/List.tsx index 9745a27d252..0db4d3e3793 100644 --- a/frontend/src/components/limitRange/List.tsx +++ b/frontend/src/components/limitRange/List.tsx @@ -7,7 +7,7 @@ import ResourceListView from '../common/Resource/ResourceListView'; export interface LimitRangeProps { limitRanges: LimitRange[] | null; - error: ApiError | null; + errors: ApiError[] | null; hideColumns?: string[]; reflectTableInURL?: SimpleTableProps['reflectInURL']; noNamespaceFilter?: boolean; @@ -15,8 +15,8 @@ export interface LimitRangeProps { export function LimitRangeRenderer(props: LimitRangeProps) { const { + errors, limitRanges, - error, hideColumns = [], reflectTableInURL = 'limitranges', noNamespaceFilter, @@ -31,7 +31,7 @@ export function LimitRangeRenderer(props: LimitRangeProps) { headerProps={{ noNamespaceFilter, }} - errorMessage={LimitRange.getErrorMessage(error)} + errors={errors} data={limitRanges} reflectInURL={reflectTableInURL} id="headlamp-limitranges" @@ -40,7 +40,7 @@ export function LimitRangeRenderer(props: LimitRangeProps) { } export function LimitRangeList() { - const [limitRanges, error] = LimitRange.useList({ namespace: useNamespaces() }); + const { items: limitRanges, errors } = LimitRange.useList({ namespace: useNamespaces() }); - return ; + return ; } diff --git a/frontend/src/components/namespace/Details.tsx b/frontend/src/components/namespace/Details.tsx index a263eed25b1..060002a82c6 100644 --- a/frontend/src/components/namespace/Details.tsx +++ b/frontend/src/components/namespace/Details.tsx @@ -69,7 +69,7 @@ export interface NamespacedLimitRangesSectionProps { export function NamespacedLimitRangesSection(props: NamespacedLimitRangesSectionProps) { const { resource } = props; - const [limitRanges, error] = LimitRange.useList({ + const { items: limitRanges, errors } = LimitRange.useList({ namespace: resource.metadata.name, }); @@ -77,7 +77,7 @@ export function NamespacedLimitRangesSection(props: NamespacedLimitRangesSection ); @@ -90,7 +90,7 @@ export interface NamespacedResourceQuotasSectionProps { export function NamespacedResourceQuotasSection(props: NamespacedResourceQuotasSectionProps) { const { resource } = props; - const [resourceQuotas, error] = ResourceQuota.useList({ + const { items: resourceQuotas, errors } = ResourceQuota.useList({ namespace: resource.metadata.name, }); @@ -98,7 +98,7 @@ export function NamespacedResourceQuotasSection(props: NamespacedResourceQuotasS ); diff --git a/frontend/src/components/pod/List.tsx b/frontend/src/components/pod/List.tsx index f4887f3b8a9..72160b0457e 100644 --- a/frontend/src/components/pod/List.tsx +++ b/frontend/src/components/pod/List.tsx @@ -10,7 +10,6 @@ import { HeadlampEventType, useEventCallback } from '../../redux/headlampEventSl import { LightTooltip, Link, SimpleTableProps } from '../common'; import { StatusLabel, StatusLabelProps } from '../common/Label'; import ResourceListView from '../common/Resource/ResourceListView'; -import { ResourceTableProps } from '../common/Resource/ResourceTable'; export function makePodStatusLabel(pod: Pod) { const phase = pod.status.phase; @@ -61,22 +60,14 @@ function getReadinessGatesStatus(pods: Pod) { export interface PodListProps { pods: Pod[] | null; - error: ApiError | null; hideColumns?: ('namespace' | 'restarts')[]; reflectTableInURL?: SimpleTableProps['reflectInURL']; noNamespaceFilter?: boolean; - clusterErrors?: ResourceTableProps['clusterErrors']; + errors?: ApiError[] | null; } export function PodListRenderer(props: PodListProps) { - const { - pods, - error, - hideColumns = [], - reflectTableInURL = 'pods', - noNamespaceFilter, - clusterErrors, - } = props; + const { pods, hideColumns = [], reflectTableInURL = 'pods', noNamespaceFilter, errors } = props; const { t } = useTranslation(['glossary', 'translation']); return ( @@ -86,7 +77,7 @@ export function PodListRenderer(props: PodListProps) { noNamespaceFilter, }} hideColumns={hideColumns} - errorMessage={Pod.getErrorMessage(error)} + errors={errors} columns={[ 'name', 'namespace', @@ -210,14 +201,13 @@ export function PodListRenderer(props: PodListProps) { ]} data={pods} reflectInURL={reflectTableInURL} - clusterErrors={clusterErrors} id="headlamp-pods" /> ); } export default function PodList() { - const { items, error, clusterErrors } = Pod.useList({ namespace: useNamespaces() }); + const { items, errors } = Pod.useList({ namespace: useNamespaces() }); const dispatchHeadlampEvent = useEventCallback(HeadlampEventType.LIST_VIEW); @@ -225,11 +215,9 @@ export default function PodList() { dispatchHeadlampEvent({ resources: items ?? [], resourceKind: 'Pod', - error: error || undefined, + error: errors?.[0] || undefined, }); - }, [items, error]); + }, [items, errors]); - return ( - - ); + return ; } diff --git a/frontend/src/components/podDisruptionBudget/__snapshots__/pdbDetails.Error.stories.storyshot b/frontend/src/components/podDisruptionBudget/__snapshots__/pdbDetails.Error.stories.storyshot index 45e720a92e9..e7d528c6071 100644 --- a/frontend/src/components/podDisruptionBudget/__snapshots__/pdbDetails.Error.stories.storyshot +++ b/frontend/src/components/podDisruptionBudget/__snapshots__/pdbDetails.Error.stories.storyshot @@ -79,7 +79,7 @@

- Error: Unreachable + TypeError: Failed to fetch

diff --git a/frontend/src/components/priorityClass/__snapshots__/priorityClassDetails.Error.stories.storyshot b/frontend/src/components/priorityClass/__snapshots__/priorityClassDetails.Error.stories.storyshot index 45e720a92e9..e7d528c6071 100644 --- a/frontend/src/components/priorityClass/__snapshots__/priorityClassDetails.Error.stories.storyshot +++ b/frontend/src/components/priorityClass/__snapshots__/priorityClassDetails.Error.stories.storyshot @@ -79,7 +79,7 @@

- Error: Unreachable + TypeError: Failed to fetch

diff --git a/frontend/src/components/resourceQuota/List.tsx b/frontend/src/components/resourceQuota/List.tsx index a560695e8d3..df0539c50f3 100644 --- a/frontend/src/components/resourceQuota/List.tsx +++ b/frontend/src/components/resourceQuota/List.tsx @@ -24,7 +24,7 @@ const PaddedChip = styled(Chip)({ export interface ResourceQuotaProps { resourceQuotas: ResourceQuota[] | null; - error: ApiError | null; + errors: ApiError[] | null; hideColumns?: string[]; reflectTableInURL?: SimpleTableProps['reflectInURL']; noNamespaceFilter?: boolean; @@ -33,7 +33,7 @@ export interface ResourceQuotaProps { export function ResourceQuotaRenderer(props: ResourceQuotaProps) { const { resourceQuotas, - error, + errors, hideColumns = [], reflectTableInURL = 'resourcequotas', noNamespaceFilter, @@ -77,7 +77,7 @@ export function ResourceQuotaRenderer(props: ResourceQuotaProps) { headerProps={{ noNamespaceFilter, }} - errorMessage={ResourceQuota.getErrorMessage(error)} + errors={errors} data={resourceQuotas} reflectInURL={reflectTableInURL} id="headlamp-resourcequotas" @@ -86,7 +86,9 @@ export function ResourceQuotaRenderer(props: ResourceQuotaProps) { } export default function ResourceQuotaList() { - const [resourceQuotas, error] = ResourceQuota.useList({ namespace: useNamespaces() }); + const { items: resourceQuotas, errors } = ResourceQuota.useList({ namespace: useNamespaces() }); - return ; + return ( + + ); } diff --git a/frontend/src/components/resourceQuota/__snapshots__/resourceQuotaDetails.Error.stories.storyshot b/frontend/src/components/resourceQuota/__snapshots__/resourceQuotaDetails.Error.stories.storyshot index 45e720a92e9..e7d528c6071 100644 --- a/frontend/src/components/resourceQuota/__snapshots__/resourceQuotaDetails.Error.stories.storyshot +++ b/frontend/src/components/resourceQuota/__snapshots__/resourceQuotaDetails.Error.stories.storyshot @@ -79,7 +79,7 @@

- Error: Unreachable + TypeError: Failed to fetch

diff --git a/frontend/src/components/role/BindingList.tsx b/frontend/src/components/role/BindingList.tsx index 81abac9437f..3d1e096e81b 100644 --- a/frontend/src/components/role/BindingList.tsx +++ b/frontend/src/components/role/BindingList.tsx @@ -2,7 +2,6 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import ClusterRoleBinding from '../../lib/k8s/clusterRoleBinding'; import RoleBinding from '../../lib/k8s/roleBinding'; -import { combineClusterListErrors, getClusterGroup } from '../../lib/util'; import { useNamespaces } from '../../redux/filterSlice'; import { Link } from '../common'; import LabelListItem from '../common/LabelListItem'; @@ -28,13 +27,10 @@ function RoleLink(props: { role: string; namespace?: string }) { export default function RoleBindingList() { const { t } = useTranslation(['glossary', 'translation']); - const { items: roles, clusterErrors: rolesErrors } = RoleBinding.useList({ + const { items: roles, errors: roleErrors } = RoleBinding.useList({ namespace: useNamespaces(), }); - const { items: clusterRoles, clusterErrors: clusterRolesErrors } = ClusterRoleBinding.useList(); - const clusters = getClusterGroup(); - - const isMultiCluster = clusters.length > 1; + const { items: clusterRoles, errors: clusterRoleErrors } = ClusterRoleBinding.useList(); const allRoles = React.useMemo(() => { if (roles === null && clusterRoles === null) { @@ -45,16 +41,12 @@ export default function RoleBindingList() { }, [roles, clusterRoles]); const allErrors = React.useMemo(() => { - return combineClusterListErrors(rolesErrors || null, clusterRolesErrors || null); - }, [rolesErrors, clusterRolesErrors]); - - function getErrorMessage() { - if (Object.values(allErrors || {}).length === clusters.length && clusters.length > 1) { - return RoleBinding.getErrorMessage(Object.values(allErrors!)[0]); + if (roleErrors === null && clusterRoleErrors === null) { + return null; } - return null; - } + return [...(roleErrors ?? []), ...(clusterRoleErrors ?? [])]; + }, [roleErrors, clusterRoleErrors]); function sortBindings(kind: string) { return function (r1: RoleBinding, r2: RoleBinding) { @@ -79,8 +71,7 @@ export default function RoleBindingList() { return ( 1; @@ -26,22 +25,17 @@ export default function RoleList() { }, [roles, clusterRoles]); const allErrors = React.useMemo(() => { - return combineClusterListErrors(rolesErrors || null, clusterRolesErrors || null); - }, [rolesErrors, clusterRolesErrors]); - - function getErrorMessage() { - if (Object.values(allErrors || {}).length === clusters.length && clusters.length > 1) { - return Role.getErrorMessage(Object.values(allErrors!)[0]); + if (rolesErrors === null && clusterRolesErrors === null) { + return null; } - return null; - } + return [...(rolesErrors ?? []), ...(clusterRolesErrors ?? [])]; + }, [rolesErrors, clusterRolesErrors]); return ( - Error: Unreachable + TypeError: Failed to fetch

diff --git a/frontend/src/i18n/locales/de/translation.json b/frontend/src/i18n/locales/de/translation.json index 67621b864bd..a3a9ab30272 100644 --- a/frontend/src/i18n/locales/de/translation.json +++ b/frontend/src/i18n/locales/de/translation.json @@ -136,7 +136,6 @@ "Finish": "Beenden", "Load from KubeConfig": "Laden aus KubeConfig", "Overview": "Übersicht", - "No permissions to list pods.": "Keine Berechtigung zum Auflisten von Pods.", "Only warnings ({{ numWarnings }})": "Nur Warnungen ({{ numWarnings }})", "Type": "Typ", "Reason": "Ereignis", diff --git a/frontend/src/i18n/locales/en/translation.json b/frontend/src/i18n/locales/en/translation.json index e340404272a..6eba112d2b9 100644 --- a/frontend/src/i18n/locales/en/translation.json +++ b/frontend/src/i18n/locales/en/translation.json @@ -136,7 +136,6 @@ "Finish": "Finish", "Load from KubeConfig": "Load from KubeConfig", "Overview": "Overview", - "No permissions to list pods.": "No permissions to list pods.", "Only warnings ({{ numWarnings }})": "Only warnings ({{ numWarnings }})", "Type": "Type", "Reason": "Reason", diff --git a/frontend/src/i18n/locales/es/translation.json b/frontend/src/i18n/locales/es/translation.json index 11952e0b87d..7945051105e 100644 --- a/frontend/src/i18n/locales/es/translation.json +++ b/frontend/src/i18n/locales/es/translation.json @@ -136,7 +136,6 @@ "Finish": "Finalizar", "Load from KubeConfig": "Cargar desde KubeConfig", "Overview": "Resumen", - "No permissions to list pods.": "Sin permisos para listar pods.", "Only warnings ({{ numWarnings }})": "Solo advertencias ({{ numWarnings }})", "Type": "Tipo", "Reason": "Razón", diff --git a/frontend/src/i18n/locales/fr/translation.json b/frontend/src/i18n/locales/fr/translation.json index 1dce870eae1..2ac78e72d9d 100644 --- a/frontend/src/i18n/locales/fr/translation.json +++ b/frontend/src/i18n/locales/fr/translation.json @@ -136,7 +136,6 @@ "Finish": "Terminer", "Load from KubeConfig": "Charger à partir d'un KubeConfig", "Overview": "Aperçu", - "No permissions to list pods.": "Pas d'autorisation pour lister les pods.", "Only warnings ({{ numWarnings }})": "Seulement les avertissements ({{ numWarnings }})", "Type": "Type", "Reason": "Motif", diff --git a/frontend/src/i18n/locales/pt/translation.json b/frontend/src/i18n/locales/pt/translation.json index f58ea939407..6cd0a36c5a2 100644 --- a/frontend/src/i18n/locales/pt/translation.json +++ b/frontend/src/i18n/locales/pt/translation.json @@ -136,7 +136,6 @@ "Finish": "Terminar", "Load from KubeConfig": "Carregar a partir de um KubeConfig", "Overview": "Resumo", - "No permissions to list pods.": "Sem permissões para listar pods", "Only warnings ({{ numWarnings }})": "Só avisos ({{ numWarnings }})", "Type": "Tipo", "Reason": "Razão", diff --git a/frontend/src/i18n/locales/zh-tw/translation.json b/frontend/src/i18n/locales/zh-tw/translation.json index ac487a86d0b..85ceaee3f34 100644 --- a/frontend/src/i18n/locales/zh-tw/translation.json +++ b/frontend/src/i18n/locales/zh-tw/translation.json @@ -136,7 +136,6 @@ "Finish": "完成", "Load from KubeConfig": "從 KubeConfig 讀取", "Overview": "概覽", - "No permissions to list pods.": "無權列出 pod。", "Only warnings ({{ numWarnings }})": "僅警告 ({{ numWarnings }})", "Type": "類型", "Reason": "原因",