Skip to content
Draft
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
7 changes: 6 additions & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,9 @@ NUXT_OAUTH_GITHUB_CLIENT_SECRET=
# HTTP_PROXY=http://proxy.company.com:8080

ENABLE_HISTORICAL_MODE=true
NUXT_PUBLIC_USE_NEW_API=true
NUXT_PUBLIC_USE_NEW_API=true

# Comma-separated list of tab names to hide from the dashboard UI.
# Available tabs: languages, editors, copilot chat, agent activity, pull requests, github.com, seat analysis, user metrics, api response
# Example: NUXT_PUBLIC_HIDDEN_TABS=api response,agent activity
NUXT_PUBLIC_HIDDEN_TABS=
11 changes: 11 additions & 0 deletions app/components/MainComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,17 @@ export default defineNuxtComponent({
}

this.config = useRuntimeConfig();

// Filter out hidden tabs based on NUXT_PUBLIC_HIDDEN_TABS environment variable
const hiddenTabsConfig = this.config.public.hiddenTabs as string;
if (hiddenTabsConfig) {
const hiddenTabs = hiddenTabsConfig.split(',').map((t: string) => t.trim().toLowerCase()).filter(Boolean);
if (hiddenTabs.length > 0) {
this.tabItems = this.tabItems.filter(
(tab: string) => !hiddenTabs.includes(tab.toLowerCase())
);
}
}
},
async mounted() {
// Load initial data
Expand Down
40 changes: 40 additions & 0 deletions e2e-tests/configurable-tabs.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { expect, test } from '@playwright/test';
import { DashboardPage } from './pages/DashboardPage';

test.describe('Configurable Tabs', () => {
test.describe.configure({ mode: 'serial' });

let dashboard: DashboardPage;

test.beforeAll(async ({ browser }) => {
const page = await browser.newPage();
await page.goto('/orgs/octo-demo-org?mock=true');

dashboard = new DashboardPage(page);

// wait for the data
await dashboard.expectMetricLabelsVisible();
});

test.afterAll(async () => {
await dashboard?.close();
});

test('should hide the "agent activity" tab when configured via NUXT_PUBLIC_HIDDEN_TABS', async () => {
const agentActivityTab = dashboard.page.getByRole('tab', { name: 'agent activity' });
await expect(agentActivityTab).not.toBeVisible();
});

test('should still show other tabs that are not hidden', async () => {
await expect(dashboard.languagesTabLink).toBeVisible();
await expect(dashboard.editorsTabLink).toBeVisible();
await expect(dashboard.copilotChatTabLink).toBeVisible();
await expect(dashboard.seatAnalysisTabLink).toBeVisible();
await expect(dashboard.userMetricsTabLink).toBeVisible();
await expect(dashboard.apiResponseTabLink).toBeVisible();
});

test('should still show the organization scope tab', async () => {
await expect(dashboard.orgTabLink).toBeVisible();
});
});
3 changes: 2 additions & 1 deletion nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ export default defineNuxtConfig({
isPublicApp: false,
// New API migration flags
useLegacyApi: false, // Set true to use deprecated /copilot/metrics API (USE_LEGACY_API)
enableHistoricalMode: false // Enable storage-backed historical queries (NUXT_PUBLIC_ENABLE_HISTORICAL_MODE)
enableHistoricalMode: false, // Enable storage-backed historical queries (NUXT_PUBLIC_ENABLE_HISTORICAL_MODE)
hiddenTabs: '' // Comma-separated list of tab names to hide (NUXT_PUBLIC_HIDDEN_TABS)
}
}
})
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "copilot-metrics-viewer",
"version": "3.0.0",
"version": "3.1.0",
"private": true,
"type": "module",
"scripts": {
Expand Down
2 changes: 2 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { isCI } from 'std-env'
// set the runtimeConfig values for the test
process.env.NUXT_PUBLIC_USING_GITHUB_AUTH = 'false'
process.env.NUXT_PUBLIC_IS_DATA_MOCKED = 'true'
// Hide 'agent activity' tab to test the configurable tabs feature
process.env.NUXT_PUBLIC_HIDDEN_TABS = 'agent activity'

export default defineConfig<ConfigOptions>({
testDir: 'e2e-tests',
Expand Down
78 changes: 78 additions & 0 deletions tests/MainComponent.hiddenTabs.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { describe, test, expect } from 'vitest'

describe('MainComponent hidden tabs filtering', () => {
const ALL_BASE_TABS = ['languages', 'editors', 'copilot chat', 'agent activity', 'pull requests', 'github.com', 'seat analysis', 'user metrics', 'api response']

function applyHiddenTabs(tabItems: string[], hiddenTabsConfig: string): string[] {
if (!hiddenTabsConfig) return tabItems
const hiddenTabs = hiddenTabsConfig.split(',').map((t: string) => t.trim().toLowerCase()).filter(Boolean)
if (hiddenTabs.length === 0) return tabItems
return tabItems.filter((tab: string) => !hiddenTabs.includes(tab.toLowerCase()))
}

test('should keep all tabs when hiddenTabs is empty', () => {
const tabs = ['organization', 'teams', ...ALL_BASE_TABS]
const result = applyHiddenTabs(tabs, '')
expect(result).toEqual(tabs)
})

test('should hide a single tab when configured', () => {
const tabs = ['organization', 'teams', ...ALL_BASE_TABS]
const result = applyHiddenTabs(tabs, 'api response')
expect(result).not.toContain('api response')
expect(result).toContain('languages')
expect(result).toContain('editors')
expect(result).toContain('seat analysis')
})

test('should hide multiple tabs when configured with comma-separated list', () => {
const tabs = ['organization', 'teams', ...ALL_BASE_TABS]
const result = applyHiddenTabs(tabs, 'api response, agent activity, pull requests')
expect(result).not.toContain('api response')
expect(result).not.toContain('agent activity')
expect(result).not.toContain('pull requests')
expect(result).toContain('languages')
expect(result).toContain('copilot chat')
expect(result).toContain('seat analysis')
expect(result).toContain('user metrics')
})

test('should be case-insensitive when matching tab names', () => {
const tabs = ['organization', ...ALL_BASE_TABS]
const result = applyHiddenTabs(tabs, 'API Response, Seat Analysis')
expect(result).not.toContain('api response')
expect(result).not.toContain('seat analysis')
expect(result).toContain('languages')
})

test('should not hide scope tabs (organization, enterprise, team)', () => {
const tabs = ['organization', 'teams', ...ALL_BASE_TABS]
// When hidden tabs config only targets content tabs, scope tabs remain
const result = applyHiddenTabs(tabs, 'api response')
expect(result).toContain('organization')
expect(result).toContain('teams')
})

test('should handle whitespace in the hidden tabs config', () => {
const tabs = ['organization', ...ALL_BASE_TABS]
const result = applyHiddenTabs(tabs, ' api response , agent activity ')
expect(result).not.toContain('api response')
expect(result).not.toContain('agent activity')
expect(result).toContain('languages')
})

test('should return empty array if all tabs are hidden', () => {
const tabs = ['languages', 'editors']
const result = applyHiddenTabs(tabs, 'languages, editors')
expect(result).toHaveLength(0)
})

test('should ignore unknown tab names in hiddenTabs config', () => {
const tabs = ['organization', ...ALL_BASE_TABS]
const result = applyHiddenTabs(tabs, 'nonexistent-tab, api response')
expect(result).not.toContain('api response')
expect(result).toContain('languages')
// Length should be original - 1 (only api response removed)
expect(result).toHaveLength(tabs.length - 1)
})
})
Loading