diff --git a/dashboard/pkg/epinio/config/epinio.ts b/dashboard/pkg/epinio/config/epinio.ts index 74d0d115..0476e4cd 100644 --- a/dashboard/pkg/epinio/config/epinio.ts +++ b/dashboard/pkg/epinio/config/epinio.ts @@ -12,13 +12,13 @@ export const BLANK_CLUSTER = '_'; // function to watch epinio route so css overrides only apply on epinio pages const watchEpinioRoute = () => { const observer = new MutationObserver(() => { - const isEpinio = window.location.pathname.includes('epinio'); + const isEpinio = window.location.pathname.startsWith('/epinio'); document.body.classList.toggle('epinio-active', isEpinio); }); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['class'] }); - document.body.classList.toggle('epinio-active', window.location.pathname.includes('epinio')); + document.body.classList.toggle('epinio-active', window.location.pathname.startsWith('/epinio')); } export function init($plugin: any, store: any) { diff --git a/dashboard/pkg/epinio/pages/c/_cluster/applications/index.vue b/dashboard/pkg/epinio/pages/c/_cluster/applications/index.vue index 81580080..ba2a1627 100644 --- a/dashboard/pkg/epinio/pages/c/_cluster/applications/index.vue +++ b/dashboard/pkg/epinio/pages/c/_cluster/applications/index.vue @@ -283,15 +283,21 @@ function handleDeleted(app: any) { onMounted(async () => { window.addEventListener('resize', onResize); - const namespaces = visibleNamespaceNames(); - - await Promise.all([ + const [,,, grouped] = await Promise.all([ store.dispatch('epinio/me'), store.dispatch('epinio/findAll', { type: EPINIO_TYPES.CONFIGURATION }), store.dispatch('epinio/findAll', { type: EPINIO_TYPES.SERVICE_INSTANCE }), - ...namespaces.map(ns => fetchNamespaceApps(ns, 1, '', false)), + store.dispatch('epinio/findGroupedApps'), ]); + // ?? {} so a failed grouped fetch degrades to an empty loop rather than throwing + for (const [ns, nsData] of Object.entries(grouped ?? {})) { + const { items, meta } = nsData as { items: any[]; meta: any }; + // spread-replace instead of direct mutation so Vue tracks the change + namespaceRows.value = { ...namespaceRows.value, [ns]: items }; + namespaceMeta.value = { ...namespaceMeta.value, [ns]: meta }; + } + pending.value = false; startPolling(['namespaces', 'configurations', 'services'], store); @@ -300,10 +306,17 @@ onMounted(async () => { appModal.value?.openCreate(); } - appsPollIntervalId = window.setInterval(() => { - visibleNamespaceNames().forEach(ns => { - fetchNamespaceApps(ns, namespaceCurrentPages.value[ns] ?? 1, searchQueries.value[ns] ?? '', true); - }); + appsPollIntervalId = window.setInterval(async() => { + // Sequential await respects per-namespace page/search state and avoids + // firing all calls simultaneously through the k8s client rate limiter. + for (const ns of visibleNamespaceNames()) { + await fetchNamespaceApps( + ns, + namespaceCurrentPages.value[ns] ?? 1, + searchQueries.value[ns] ?? '', + true, + ); + } }, APPS_POLL_RATE_MS); }); @@ -336,7 +349,7 @@ onUnmounted(() => { -