diff --git a/app/components/MainComponent.vue b/app/components/MainComponent.vue
index 66b7c3dc..d7ea5eef 100644
--- a/app/components/MainComponent.vue
+++ b/app/components/MainComponent.vue
@@ -216,7 +216,7 @@
-
+
@@ -321,7 +321,7 @@ import AiChatPanel from './AiChatPanel.vue'
import AdminPanel from './AdminPanel.vue'
import { Options } from '@/model/Options';
import { useRoute } from 'vue-router';
-import { applyHiddenTabs, applyHistoricalModeFilter } from '@/utils/tabUtils';
+import { applyHiddenTabs, applyHistoricalModeFilter, shouldShowActiveTabLoading } from '@/utils/tabUtils';
import { routeParamStr } from '@/utils/routeUtils';
import { resolveDisplayName } from '#shared/utils/resolveDisplayName';
@@ -352,6 +352,15 @@ export default defineNuxtComponent({
showAnnouncementBanner(): boolean {
return !!this.announcementMessage && !this.announcementDismissed;
},
+ showActiveTabLoading(): boolean {
+ return shouldShowActiveTabLoading({
+ tab: this.tab,
+ signInRequired: this.signInRequired,
+ metricsReady: this.metricsReady,
+ seatsReady: this.seatsReady,
+ userMetricsReady: this.userMetricsReady,
+ });
+ },
},
methods: {
dismissAnnouncement() {
diff --git a/app/utils/tabUtils.ts b/app/utils/tabUtils.ts
index e57e8fcf..49db7565 100644
--- a/app/utils/tabUtils.ts
+++ b/app/utils/tabUtils.ts
@@ -22,3 +22,29 @@ export function applyHiddenTabs(tabItems: string[], hiddenTabsConfig: string): s
export function applyHistoricalModeFilter(tabItems: string[], _enableHistoricalMode: boolean | string): string[] {
return tabItems;
}
+
+export interface ActiveTabLoadingState {
+ tab: string | null;
+ signInRequired: boolean;
+ metricsReady: boolean;
+ seatsReady: boolean;
+ userMetricsReady: boolean;
+}
+
+/**
+ * Returns whether the shared loading indicator should be visible for the
+ * currently active tab.
+ */
+export function shouldShowActiveTabLoading(state: ActiveTabLoadingState): boolean {
+ if (state.signInRequired) return false;
+
+ if (state.tab === 'seat analysis') {
+ return !state.seatsReady;
+ }
+
+ if (state.tab === 'user metrics') {
+ return !state.userMetricsReady;
+ }
+
+ return !state.metricsReady;
+}
diff --git a/tests/tabUtils.loading.spec.ts b/tests/tabUtils.loading.spec.ts
new file mode 100644
index 00000000..52d5cb30
--- /dev/null
+++ b/tests/tabUtils.loading.spec.ts
@@ -0,0 +1,44 @@
+import { describe, expect, test } from 'vitest'
+import { shouldShowActiveTabLoading } from '../app/utils/tabUtils'
+
+describe('shouldShowActiveTabLoading', () => {
+ test('shows loading for seat analysis while seats data is not ready', () => {
+ expect(shouldShowActiveTabLoading({
+ tab: 'seat analysis',
+ signInRequired: false,
+ metricsReady: true,
+ seatsReady: false,
+ userMetricsReady: true,
+ })).toBe(true)
+ })
+
+ test('shows loading for user metrics while user metrics data is not ready', () => {
+ expect(shouldShowActiveTabLoading({
+ tab: 'user metrics',
+ signInRequired: false,
+ metricsReady: true,
+ seatsReady: true,
+ userMetricsReady: false,
+ })).toBe(true)
+ })
+
+ test('does not show loading on seat analysis when seats data is ready', () => {
+ expect(shouldShowActiveTabLoading({
+ tab: 'seat analysis',
+ signInRequired: false,
+ metricsReady: true,
+ seatsReady: true,
+ userMetricsReady: true,
+ })).toBe(false)
+ })
+
+ test('does not show loading when sign in is required', () => {
+ expect(shouldShowActiveTabLoading({
+ tab: 'seat analysis',
+ signInRequired: true,
+ metricsReady: false,
+ seatsReady: false,
+ userMetricsReady: false,
+ })).toBe(false)
+ })
+})