Add personal Copilot analytics to the Copilot tab and document required token access - #20
Conversation
Agent-Logs-Url: https://github.com/thinkdj/gh.devcp/sessions/c04af5d6-e865-425a-bcac-b0193097cebb Co-authored-by: thinkdj <688055+thinkdj@users.noreply.github.com>
Agent-Logs-Url: https://github.com/thinkdj/gh.devcp/sessions/c04af5d6-e865-425a-bcac-b0193097cebb Co-authored-by: thinkdj <688055+thinkdj@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds personal-account Copilot analytics to the Copilot tab (via user billing usage endpoints) while preserving the existing org analytics flow, and documents the required token access.
Changes:
- Introduces unified Copilot analytics “targets” (org + personal) with a selector and shared panel rendering.
- Adds GitHub API support + types for personal billing usage and premium request usage, with partial-failure handling.
- Expands tests and README/token settings copy to cover personal analytics and required permissions.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/types.ts | Adds personal billing usage/premium request types and a union type for org vs personal analytics data. |
| src/github-api.ts | Adds fetchers for personal billing usage + premium request usage, and returns normalized type/target metadata for org data. |
| src/github-api.test.ts | Adds unit tests for personal analytics fetcher and partial-failure behavior. |
| src/demo-data.ts | Updates demo org Copilot payload to include new type/target fields. |
| src/components/TokenSettings.tsx | Updates in-app token guidance for Copilot personal + org analytics access. |
| src/components/CopilotMetricsPanel.tsx | Adds account selector and personal analytics rendering/aggregation alongside existing org metrics UI. |
| src/App.tsx | Switches Copilot tab to a single query keyed by selected target (org/personal) and passes new props to the panel. |
| src/App.test.tsx | Adds coverage for personal-only analytics and switching between org/personal targets. |
| README.md | Documents token permissions/scopes for PR/branch features plus Copilot org + personal analytics. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| let totalUsageQuantity = 0 | ||
| let totalPremiumRequests = 0 | ||
| let totalNetAmount = 0 | ||
|
|
||
| for (const item of usage) { | ||
| const quantity = item.quantity ?? item.netQuantity ?? item.grossQuantity ?? 0 | ||
| totalUsageQuantity += quantity | ||
| totalNetAmount += item.netAmount ?? 0 | ||
|
|
||
| const key = `${item.product} · ${item.sku}` | ||
| const existing = productMap.get(key) ?? { quantity: 0, netAmount: 0, unitType: item.unitType } | ||
| existing.quantity += quantity | ||
| existing.netAmount += item.netAmount ?? 0 | ||
| productMap.set(key, existing) | ||
|
|
||
| if (item.repositoryName) { | ||
| const repoExisting = repoMap.get(item.repositoryName) ?? { quantity: 0, netAmount: 0 } | ||
| repoExisting.quantity += quantity | ||
| repoExisting.netAmount += item.netAmount ?? 0 | ||
| repoMap.set(item.repositoryName, repoExisting) | ||
| } | ||
| } | ||
|
|
||
| for (const item of premiumRequests) { | ||
| const quantity = item.netQuantity ?? item.quantity ?? item.grossQuantity ?? 0 | ||
| totalPremiumRequests += quantity | ||
| const key = item.model ? `${item.product} · ${item.model}` : `${item.product} · ${item.sku}` | ||
| const existing = premiumMap.get(key) ?? { quantity: 0, netAmount: 0 } | ||
| existing.quantity += quantity | ||
| existing.netAmount += item.netAmount ?? 0 | ||
| premiumMap.set(key, existing) | ||
| } |
There was a problem hiding this comment.
aggregatePersonalUsage’s totalNetAmount is only incremented for usage items, but premium request items also have netAmount. This makes the “Net spend” summary undercount whenever premium requests are non-zero. Consider adding premium request netAmount into the total (or rename the card/label to clarify it excludes premium requests).
| @@ -1,4 +1,5 @@ | |||
| import { useMemo, useState } from 'react' | |||
| import type { ReactNode } from 'react' | |||
There was a problem hiding this comment.
This file now imports ReactNode, but BreakdownTable still types icon as React.ReactNode. In this repo, the React namespace isn’t guaranteed to be available (and you’ve already switched SummaryCard to ReactNode), so this can reintroduce a type error. Prefer using the imported ReactNode here as well, or import the React namespace type explicitly.
| import type { ReactNode } from 'react' | |
| import type { ReactNode } from 'react' | |
| import type * as React from 'react' |
Agent-Logs-Url: https://github.com/thinkdj/gh.devcp/sessions/d26275e0-0171-48af-babd-ea2cc66393a7 Co-authored-by: thinkdj <688055+thinkdj@users.noreply.github.com>
filterCopilotBillingItems— return empty list when no Copilot items matchnetAmountintotalNetAmountinaggregatePersonalUsageReact.ReactNodewith the importedReactNodeinBreakdownTable