Skip to content

Commit a13c75c

Browse files
committed
Merge branch 'main' into copilot/fix-244
# Please enter a commit message to explain why this merge is necessary, # especially if it merges an updated upstream into a topic branch. # # Lines starting with '#' will be ignored, and an empty message aborts # the commit.
2 parents 0077bba + 5966740 commit a13c75c

15 files changed

+1528
-148
lines changed

.github/copilot-instructions.md

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
# GitHub Copilot Metrics Viewer
2+
3+
GitHub Copilot Metrics Viewer is a Nuxt 3 web application that displays GitHub Copilot usage metrics and analytics for organizations and enterprises. The application visualizes data from the GitHub Copilot Metrics API using Vue.js, TypeScript, Vuetify, and Chart.js.
4+
5+
Always reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.
6+
7+
## Working Effectively
8+
9+
### Initial Setup
10+
- **Node.js requirement**: Uses Node.js 20+ (verified: v20.19.4 works)
11+
- Install dependencies: `npm install`
12+
- **NEVER CANCEL**: Takes 3 minutes to complete. Set timeout to 5+ minutes.
13+
- Includes postinstall script that runs `nuxt prepare`
14+
15+
### Build and Development
16+
- **Development server**: `npm run dev`
17+
- Starts on http://localhost:3000/
18+
- **Font provider warnings are normal** - application works despite "Could not fetch fonts" errors
19+
- Supports hot reload and auto-refresh
20+
- **Production build**: `npm run build`
21+
- **NEVER CANCEL**: Takes 30 seconds to complete. Set timeout to 2+ minutes.
22+
- Builds successfully despite font provider connection warnings
23+
- Outputs to `.output/` directory
24+
- **Production preview**: Built server requires proper environment setup
25+
- After build: `NUXT_SESSION_PASSWORD=something_long_and_random_thats_at_least_32_characters node .output/server/index.mjs`
26+
- **NOTE**: Health endpoints may not work correctly in built mode in some environments
27+
- **Recommendation**: Use `npm run dev` for development and testing validation scenarios
28+
29+
### Testing
30+
- **Unit tests**: `npm test` (using Vitest)
31+
- **NEVER CANCEL**: Takes 15 seconds to complete. Set timeout to 2+ minutes.
32+
- Runs 83 tests, all should pass
33+
- Uses mocked data environment
34+
- **E2E tests**: `npm run test:e2e` (using Playwright)
35+
- **NOTE**: Playwright browser installation may fail in some environments due to download issues
36+
- Install browsers first: `npx playwright install`
37+
- Uses mocked data for testing
38+
- **Type checking**: `npm run typecheck`
39+
- **KNOWN ISSUE**: Currently fails with 18 TypeScript errors
40+
- Takes 10 seconds to complete
41+
- Errors are in existing codebase, not blocking for development
42+
43+
### Code Quality
44+
- **Linting**: `npm run lint`
45+
- **KNOWN ISSUE**: Currently fails with 43 ESLint errors (mostly @typescript-eslint/no-explicit-any)
46+
- Takes 3 seconds to complete
47+
- `npm run lint:fix` can fix some formatting issues but not the core errors
48+
- **Always run linting** but expect failures in current codebase
49+
50+
## Environment Configuration
51+
52+
### Required Environment Variables
53+
- **NUXT_SESSION_PASSWORD**: Required, minimum 32 characters
54+
- Used for session encryption
55+
- Example: `NUXT_SESSION_PASSWORD=something_long_and_random_thats_at_least_32_characters`
56+
57+
### GitHub Integration
58+
- **Mock mode (default)**: `NUXT_PUBLIC_IS_DATA_MOCKED=true`
59+
- Works without GitHub tokens
60+
- Uses sample data for development and testing
61+
- **Real GitHub data**: Requires GitHub Personal Access Token
62+
- `NUXT_GITHUB_TOKEN=<your_token>`
63+
- Token needs scopes: copilot, manage_billing:copilot, manage_billing:enterprise, read:enterprise, read:org
64+
65+
### Scope Configuration
66+
- **NUXT_PUBLIC_SCOPE**: Sets default scope ('organization', 'enterprise', 'team-organization', 'team-enterprise')
67+
- **NUXT_PUBLIC_GITHUB_ORG**: Target organization name
68+
- **NUXT_PUBLIC_GITHUB_ENT**: Target enterprise name
69+
- **NUXT_PUBLIC_GITHUB_TEAM**: Target team name (optional)
70+
71+
### OAuth Configuration (Optional)
72+
- **NUXT_PUBLIC_USING_GITHUB_AUTH**: Enable GitHub OAuth (default: false)
73+
- **NUXT_OAUTH_GITHUB_CLIENT_ID**: GitHub App client ID
74+
- **NUXT_OAUTH_GITHUB_CLIENT_SECRET**: GitHub App client secret
75+
76+
## Validation
77+
78+
### Manual Testing Scenarios
79+
Always test these scenarios after making changes (use development mode for reliable validation):
80+
81+
1. **Health Check Endpoints** (use dev server: `npm run dev`):
82+
- Test: `curl http://localhost:3000/api/health`
83+
- Expected: JSON response with status, timestamp, version, uptime
84+
- Test: `curl http://localhost:3000/api/ready`
85+
- Expected: JSON response with status, checks object
86+
- Test: `curl http://localhost:3000/api/live`
87+
- Expected: JSON response with status, memory usage, process info
88+
89+
2. **Mock Data Functionality**:
90+
- Start dev server: `npm run dev`
91+
- Navigate to: http://localhost:3000/orgs/mocked-org?mock=true
92+
- Verify: Page loads showing metrics dashboard with charts
93+
- Test language breakdown, seat analysis, and chat metrics tabs
94+
95+
3. **Different Scope URLs**:
96+
- Organizations: `http://localhost:3000/orgs/octodemo`
97+
- Enterprises: `http://localhost:3000/enterprises/octo-demo-ent`
98+
- Teams: `http://localhost:3000/orgs/octodemo/teams/the-a-team`
99+
100+
### Docker Support
101+
- **Build**: `docker build -t copilot-metrics-viewer .`
102+
- **NOTE**: May fail in environments with certificate/proxy issues
103+
- Uses multi-stage build with Node.js Alpine images
104+
- **Playwright mode**: `docker build -t copilot-metrics-pw --build-arg mode=playwright .`
105+
- **Run**: See DEPLOYMENT.md for full Docker configuration examples
106+
107+
### Always Run Before Committing
108+
1. **Build verification**: `npm run build` - Must complete successfully
109+
2. **Unit tests**: `npm test` - All 83 tests must pass
110+
3. **Basic functionality**: Start dev server and verify health endpoints respond
111+
4. **Linting awareness**: Run `npm run lint` (expect existing errors, don't introduce new ones)
112+
113+
## Common Tasks
114+
115+
### Repo Structure
116+
```
117+
├── app/ # Vue.js application source
118+
│ ├── components/ # Vue components (MetricsViewer, SeatsAnalysisViewer, etc.)
119+
│ ├── pages/ # Nuxt pages (index.vue)
120+
│ ├── model/ # TypeScript data models
121+
│ └── utils/ # Utility functions
122+
├── server/ # Nuxt server-side code
123+
│ ├── api/ # API endpoints (health.ts, metrics.ts, seats.ts)
124+
│ ├── routes/ # Server routes (auth)
125+
│ └── plugins/ # Server plugins (http-agent.ts)
126+
├── tests/ # Unit tests (Vitest)
127+
├── e2e-tests/ # End-to-end tests (Playwright)
128+
├── .env # Environment configuration
129+
├── nuxt.config.ts # Nuxt configuration
130+
├── package.json # Dependencies and scripts
131+
└── Dockerfile # Container configuration
132+
```
133+
134+
### Key Files to Monitor
135+
- **Health endpoints**: `/server/api/health.ts`, `/server/api/ready.ts`, `/server/api/live.ts`
136+
- **Main metrics logic**: `/server/api/metrics.ts`, `/server/api/seats.ts`
137+
- **Frontend components**: `/app/components/MetricsViewer.vue`, `/app/components/MainComponent.vue`
138+
- **Configuration**: `/nuxt.config.ts`, `/.env`
139+
140+
### Debugging Tips
141+
- **Font provider warnings**: Normal in restricted network environments, application functions correctly
142+
- **Mock data**: Use `?mock=true` query parameter for testing without GitHub tokens
143+
- **API debugging**: Check browser network tab for API call responses
144+
- **Server logs**: Development server shows detailed request logs and errors
145+
146+
### Performance Notes
147+
- **Development startup**: ~10 seconds with font provider retries
148+
- **Build time**: ~30 seconds
149+
- **Test execution**: ~15 seconds for full unit test suite
150+
- **Hot reload**: Very fast in development mode
151+
152+
## Known Limitations
153+
- **Linting**: 43 existing ESLint errors in codebase (mostly TypeScript any types)
154+
- **Type checking**: 18 existing TypeScript errors
155+
- **Playwright**: Browser installation may fail in restricted environments
156+
- **Docker**: Build may fail in environments with certificate/proxy restrictions
157+
- **Font providers**: External font API calls fail in restricted networks (non-blocking)
158+
159+
Always validate your changes work in mock mode first, then test with real GitHub data if available.

.vscode/settings.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"files.watcherExclude": {
3+
"**/node_modules/**": true,
4+
"**/.git/**": true,
5+
"**/dist/**": true
6+
}
7+
}

app/assets/global.css

Lines changed: 124 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
/* App theme variables */
2+
:root {
3+
/* Use Vuetify primary as the single source of truth for accent */
4+
--app-accent: var(--v-theme-primary, #1976D2);
5+
/* Fallback translucent variants (based on #1976D2) */
6+
--app-accent-weak: rgba(25, 118, 210, 0.10); /* subtle bg */
7+
--app-accent-overlay: rgba(25, 118, 210, 0.12); /* tab overlay */
8+
}
9+
10+
/* Prefer dynamic derivation when supported */
11+
@supports (color: color-mix(in srgb, red, transparent)) {
12+
:root {
13+
--app-accent-weak: color-mix(in srgb, var(--app-accent), transparent 90%);
14+
--app-accent-overlay: color-mix(in srgb, var(--app-accent), transparent 88%);
15+
}
16+
}
17+
118
.tiles-container {
219
display: flex;
320
justify-content: flex-start;
@@ -34,7 +51,7 @@
3451
left: 0 !important;
3552
width: 100% !important;
3653
height: 100% !important;
37-
background-color: rgba(44, 32, 157, 0.557) !important;
54+
background-color: var(--app-accent-overlay) !important;
3855
pointer-events: none !important;
3956
z-index: 1 !important;
4057
}
@@ -60,7 +77,7 @@
6077

6178
/* Fix button hover styles - light gray background */
6279
.v-btn:hover {
63-
background-color: rgba(63, 81, 181, 0.1) !important;
80+
background-color: var(--app-accent-weak) !important;
6481
}
6582

6683
.v-btn:hover .v-btn__overlay {
@@ -75,4 +92,109 @@
7592
/* Override Vuetify's default overlay behavior for variant-text buttons in tabs */
7693
.v-tab .v-btn--variant-text .v-btn__overlay {
7794
background: transparent !important;
95+
}
96+
97+
/* Teams dropdown menu: global surface fallback and panel styling */
98+
.v-overlay__content.teams-select-menu {
99+
background-color: var(--v-theme-surface, #fff) !important;
100+
border: 1px solid var(--app-accent-weak);
101+
box-shadow: 0 6px 24px rgba(56, 79, 184, 0.15);
102+
border-radius: 8px;
103+
max-height: 360px;
104+
overflow-y: auto;
105+
min-width: 320px;
106+
z-index: 2000;
107+
}
108+
109+
/* Hover highlight inside teams dropdown aligns with tab/button accent */
110+
.v-overlay__content.teams-select-menu .v-list-item:hover {
111+
background-color: var(--app-accent-weak) !important;
112+
}
113+
114+
/* Ensure readable black text and remove dark overlay on items */
115+
.v-overlay__content.teams-select-menu .v-list-item,
116+
.v-overlay__content.teams-select-menu .v-list-item .v-list-item-title,
117+
.v-overlay__content.teams-select-menu .v-list-item .v-list-item-subtitle,
118+
.v-overlay__content.teams-select-menu .v-list-item .v-icon,
119+
.v-overlay__content.teams-select-menu .v-list-item .v-selection-control .v-icon {
120+
color: #000 !important;
121+
}
122+
.v-overlay__content.teams-select-menu .v-list-item .v-list-item__overlay,
123+
.v-overlay__content.teams-select-menu .v-list-item--active .v-list-item__overlay {
124+
background: transparent !important;
125+
}
126+
/* Keep selected item highlighted with same light blue as hover */
127+
.v-overlay__content.teams-select-menu .v-list-item--active {
128+
background-color: var(--app-accent-weak) !important;
129+
}
130+
131+
.v-overlay__content.teams-select-menu .v-list {
132+
padding: 8px 0;
133+
}
134+
135+
.v-overlay__content.teams-select-menu .v-list-item {
136+
min-height: 40px;
137+
}
138+
139+
/* Chips palette: light primary tint background + black text/icons */
140+
.v-chip.select-chip,
141+
.v-chip.selected-team-chip {
142+
background-color: var(--app-accent-weak) !important;
143+
color: #000 !important;
144+
border-color: var(--app-accent-weak) !important;
145+
background-image: none !important;
146+
}
147+
.v-chip.select-chip .v-chip__overlay,
148+
.v-chip.selected-team-chip .v-chip__overlay {
149+
background: transparent !important;
150+
}
151+
.v-chip.select-chip .v-chip__underlay,
152+
.v-chip.selected-team-chip .v-chip__underlay {
153+
background-color: var(--app-accent-weak) !important;
154+
}
155+
.v-chip.select-chip:hover .v-chip__overlay,
156+
.v-chip.selected-team-chip:hover .v-chip__overlay {
157+
background: transparent !important;
158+
}
159+
.v-chip.select-chip .v-icon,
160+
.v-chip.selected-team-chip .v-icon,
161+
.v-chip.select-chip .v-chip__content,
162+
.v-chip.selected-team-chip .v-chip__content {
163+
color: #000 !important;
164+
}
165+
166+
/* Data table pagination & items-per-page menu styling */
167+
/* Ensure pagination toolbar inherits surface background and consistent spacing */
168+
.v-data-table .v-data-table-footer {
169+
background-color: var(--v-theme-surface, #fff) !important;
170+
}
171+
172+
/* Items-per-page activator button */
173+
.v-data-table .v-data-table-footer .v-field {
174+
min-width: 90px;
175+
}
176+
177+
/* Dropdown menu for items-per-page (was showing black background entries) */
178+
.v-overlay__content .v-list-item[role="option"] {
179+
background: #fff !important;
180+
color: #000 !important;
181+
}
182+
.v-overlay__content .v-list-item[role="option"] .v-list-item-title {
183+
color: #000 !important;
184+
}
185+
.v-overlay__content .v-list-item[role="option"]:hover {
186+
background: var(--app-accent-weak) !important;
187+
}
188+
.v-overlay__content .v-list-item[role="option"].v-list-item--active {
189+
background: var(--app-accent-weak) !important;
190+
}
191+
192+
/* Remove dark overlay layer inside those list items */
193+
.v-overlay__content .v-list-item[role="option"] .v-list-item__overlay {
194+
background: transparent !important;
195+
}
196+
197+
/* Pagination buttons hover state unify with buttons */
198+
.v-data-table .v-data-table-footer .v-btn:hover {
199+
background-color: var(--app-accent-weak) !important;
78200
}

app/components/AgentModeViewer.vue

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@
140140
</v-card>
141141
</v-tooltip>
142142
<div class="chart-container">
143-
<LineChart v-if="stats.agentModeChartData.labels.length" :data="stats.agentModeChartData"
143+
<LineChart
144+
v-if="stats.agentModeChartData.labels.length" :data="stats.agentModeChartData"
144145
:options="chartOptions" />
145146
</div>
146147

@@ -166,7 +167,8 @@
166167
IDE Code Completions Models ({{ stats.ideCodeCompletionModels.length }})
167168
</v-expansion-panel-title>
168169
<v-expansion-panel-text>
169-
<v-data-table :headers="codeCompletionHeaders" :items="stats.ideCodeCompletionModels"
170+
<v-data-table
171+
:headers="codeCompletionHeaders" :items="stats.ideCodeCompletionModels"
170172
class="elevation-1" item-key="name" />
171173
</v-expansion-panel-text>
172174
</v-expansion-panel>
@@ -177,7 +179,8 @@
177179
IDE Chat Models ({{ stats.ideChatModels.length }})
178180
</v-expansion-panel-title>
179181
<v-expansion-panel-text>
180-
<v-data-table :headers="ideChatHeaders" :items="stats.ideChatModels" class="elevation-1"
182+
<v-data-table
183+
:headers="ideChatHeaders" :items="stats.ideChatModels" class="elevation-1"
181184
item-key="name" />
182185
</v-expansion-panel-text>
183186
</v-expansion-panel>
@@ -188,7 +191,8 @@
188191
GitHub.com Chat Models ({{ stats.dotcomChatModels.length }})
189192
</v-expansion-panel-title>
190193
<v-expansion-panel-text>
191-
<v-data-table :headers="dotcomChatHeaders" :items="stats.dotcomChatModels"
194+
<v-data-table
195+
:headers="dotcomChatHeaders" :items="stats.dotcomChatModels"
192196
class="elevation-1" item-key="name" />
193197
</v-expansion-panel-text>
194198
</v-expansion-panel>
@@ -199,7 +203,8 @@
199203
GitHub.com PR Summary Models ({{ stats.dotcomPRModels.length }})
200204
</v-expansion-panel-title>
201205
<v-expansion-panel-text>
202-
<v-data-table :headers="dotcomPRHeaders" :items="stats.dotcomPRModels"
206+
<v-data-table
207+
:headers="dotcomPRHeaders" :items="stats.dotcomPRModels"
203208
class="elevation-1" item-key="name" />
204209
</v-expansion-panel-text>
205210
</v-expansion-panel>
@@ -218,7 +223,8 @@
218223
</v-card>
219224
</v-tooltip>
220225
<div class="chart-container">
221-
<BarChart v-if="stats.modelUsageChartData.labels.length" :data="stats.modelUsageChartData"
226+
<BarChart
227+
v-if="stats.modelUsageChartData.labels.length" :data="stats.modelUsageChartData"
222228
:options="barChartOptions" />
223229
</div>
224230
</div>

0 commit comments

Comments
 (0)