Skip to content

feat(usage): Cursor-style Usage tab for the workspace, each person and you - #574

Merged
Deodat-Lawson merged 6 commits into
mainfrom
claude/usage-tab-design-2e1613
Nov 1, 2026
Merged

Deodat-Lawson merged 6 commits into
mainfrom
claude/usage-tab-design-2e1613

Conversation

@Deodat-Lawson

@Deodat-Lawson Deodat-Lawson commented Nov 1, 2026 •

Copy link
Copy Markdown
Owner

A Usage tab modelled on Cursor's team dashboard: the whole workspace, each person in it, and every member's own numbers.

What it does

A Studio tab with its screens across the top. Owners and admins (analytics.view) get every screen. Everyone else gets You, whatever link they follow.

Screen What it shows
Overview Active people, questions asked, files added, documents opened, AI tokens and estimated cost, each with its change from the previous window. A daily chart with a switch for what it measures, then by feature, most active people and by model. Can be filtered by group.
People A leaderboard that sorts and filters, with each person's limit for this month. A row opens that person's own page.
Activity AI use by day, person, feature and route, with filters kept in the URL.
Documents Which documents get opened and by how many people, with the existing per-document sheet.
You Your own numbers next to the workspace median, which is shown only when the workspace has 3 or more members, so it never names or gives away anyone else's numbers. Also your streak and your AI limit.
Spending Workspace › Usage as it was: balance, burn, ledger, pricing and per-person limits.

Every screen except Spending shares one date window (7, 30 or 90 days, or a custom range) and has Export CSV. Cells that look like spreadsheet formulas are exported as plain text. All copy is in English and Chinese.

Moves

  • Workspace › Data › Usage is now Usage › Spending. #usage… hashes and ?feature=workspace&at=/usage redirect there. The "raise your limit" copy in chat and in error messages now points to Usage › Spending.
  • The Analytics app is folded in. Its employee table became People and its document table became Documents. ?feature=analytics, #analytics and /employer/statistics open the Overview. StatisticsView and /api/company/analysis-dashboard are deleted.

How it's built

  • Five read-only routes: /api/usage/{summary,people,activity,documents,me}, backed by src/server/usage.
    • Built from what is already recorded per person and UTC day: ai_usage_daily, Ask turns in workspace_session_messages, workspace_activity and document_views.
    • No schema change.
  • Access: summary, people, activity and documents require analytics.view. me answers any member with their own numbers only.
  • Shared contract: src/lib/usage/{types,range,csv}.ts.

Verification

  • Live on a private demo DB (12 people, 120 days):
    • Overview totals match direct SQL exactly for 7, 30 and 90 days.
    • People rows + people who have left + "no one person" add up to the workspace totals.
    • A member gets 403 from every company-wide route.
    • Exports total to the same tokens as the screen.
    • Old links land on the new screens.
  • Independent QA passed on round 3. It found 12 minor issues and no blockers; 11 are fixed in this branch.
  • Checks:
    • next build passed before the merge with main, which was clean; CI builds the merged branch.
    • tsc and eslint are clean.
    • The affected suites pass (24 suites, 437 tests), including i18n parity, the service map and the permission gates.
    • The DB integration suite (usage.integration.test.ts) passed 7/7 against a fresh database.

Known limitations

🤖 Generated with Claude Code


Note

Medium Risk
New analytics APIs and rollup logic affect how usage and costs are shown and who can see whom; boundaries are permission-gated and heavily tested, but mistakes could leak cross-member data or mis-total workspace usage.

Overview
Introduces a dedicated Usage Studio tab (Overview, People, Activity, Documents, You, Spending) backed by five read-only /api/usage/* endpoints and a new server/usage aggregation layer over existing daily meters, session messages, activity, and document views—no schema changes.

Analytics and the old dashboard are removed: StatisticsView, GET /api/company/analysis-dashboard, and their tests go away; #analytics / ?feature=analytics now open Usage Overview. Workspace › Usage moves to Usage Spending; settings hashes and legacy workspace /usage paths redirect there, and AI limit copy points to Usage › Spending.

Access: workspace-wide routes stay gated on analytics.view; /api/usage/me is open to any member for self-only data. Permission-gate and route tests are updated accordingly; large unit, integration, and UI test coverage is added for rollups, CSV export, date windows, and the Usage tool.

Reviewed by Cursor Bugbot for commit be88348. Bugbot is set up for automated code reviews on this repo. Configure here.

Deodat-Lawson and others added 6 commits October 28, 2026 00:31
Five GET routes for the Usage tab, built on what is already recorded per
person and UTC day: AI calls (ai_usage_daily), questions (Ask turns in
workspace_session_messages), files added (workspace_activity) and documents
opened (document_views).

- summary (workspace, one person or a group), people (the leaderboard with
  this month's limit), activity (AI by day, person, feature and route) and
  documents gate on analytics.view; me answers any member with their own
  numbers, the workspace median and their streak, never anyone else's.
- People + people who have left + work no one started add up to the
  workspace totals; a test proves it.
- The Spending screen's ledger keeps server/settings/usage; its model half
  moved here, and the old analysis-dashboard route (Analytics) is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Studio tab with its screens across the top. Owners and admins
(analytics.view) get Overview, People, Activity, Documents, You and
Spending; everyone else gets You, wherever a link points.

- Overview: active people, questions asked, files added, documents opened,
  AI tokens and estimated cost, each against the window before; a daily
  chart with a measure switch; by feature, by model; the most active.
- People: the leaderboard, sortable and searchable, with this month's AI
  limit; a row opens that person's numbers.
- Activity: AI use by day, person, feature and route.
- Documents: what gets opened and by how many people (the Analytics app's
  table and sheet; Analytics folds in here and its links land on Overview).
- You: your numbers beside the workspace median, your streak and limit.
- Spending: Workspace > Usage as it was, minus the model tables that moved
  to Overview; #usage links and ?feature=workspace&at=/usage land here.
- 7/30/90 days or a custom range and a group filter carry across screens;
  every screen exports what it shows as CSV (formula cells neutralised).
- Words in usage.json (en + zh-CN); old "Workspace > Usage and costs"
  copy now says Usage > Spending.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- /me withholds the median below three members: with two it is their
  average, and your own figure would give the other person's away.
- A window that ends before today shows its longest run of active days,
  not today's streak; Documents counts only what existed by its end.
- A filtered People export holds only the rows shown.
- #analytics / #statistics always open the Overview.
- The custom range says which rule it breaks; limits round down like
  Spending; at phone width People keeps AI tokens in view.
- The manual testing guide's Statistics section is the Usage tab now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…streak caption

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Nov 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
pdr-ai-v2 Ignored Ignored Nov 1, 2026 8:13pm UTC

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-01T20:15:20.646501Z be88348 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: be88348c24

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

),
[data, needle]
);
const opened = data?.documents.filter(doc => doc.views > 0).length ?? 0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Count opened documents across the full document scope

When more than 200 visible documents have been opened in the selected window, this count is capped at 200 because /api/usage/documents returns only the first 200 document rows by default. The Opened tile and its percentage nevertheless use this count against the uncapped totalDocuments, so a workspace where all 500 documents were opened reports 200 opened and 40% instead of 500 and 100%. Return an uncapped opened-document aggregate from the query rather than deriving this workspace-wide metric from the limited list.

Useful? React with 👍 / 👎.

Comment on lines +113 to +114
const page = await fetchPage(rows.length, PAGE);
setMore(prev => [...prev, ...page.rows]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Discard pagination responses after activity filters change

If someone clicks Show more and changes the person, feature, or date window before that request finishes, the query-change effect clears more, but this pending request subsequently appends rows from the old query. Those rows remain mixed into the newly filtered activity, and subsequent pagination uses the mixed row count as its offset, potentially skipping valid results. Capture and verify the query generation before appending, or cancel outstanding pagination requests when the query changes.

Useful? React with 👍 / 👎.

Comment on lines +69 to +73
const [choice, setChoice] = useState<WindowChoice>(() => choiceFromQuery(params));
const [groupId, setGroupId] = useState<number | null>(() => {
const raw = params.get("group");
return raw && /^\d+$/.test(raw) ? Number(raw) : null;
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Synchronize shared usage filters with tool navigation

Changing the window or group only updates React state; it never updates the tool's search parameters. The frame's Copy link action builds its URL from path and search in ToolFrame.tsx, so selecting 7 days or a group and copying the screen still shares the old filters (usually the default 30 days and everyone). Conversely, because these query parameters are read only during initialization and the Usage tab stays mounted, opening a later deep link with an explicit window or group does not apply those filters. Synchronize explicitly supplied filters and filter changes with the tool location while retaining them during ordinary screen navigation.

Useful? React with 👍 / 👎.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit be88348. Configure here.

activePeople: people.size,
activeDays: days.size,
};
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Active people includes departed members

Medium Severity

Workspace activePeople counts every non-empty userId in the window, including people who have left, while members is only current members. Overview renders those two figures as one ratio, so a window with departed activity can show an impossible value such as more active people than members.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit be88348. Configure here.

return;
}
setData((await res.json()) as T);
setFailed(null);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale usage fetch overwrites newer data

Medium Severity

useUsageLoad drops an older response only before res.json(). After that await, a slower earlier request can still call setData and clear failed, replacing a newer window’s numbers with a previous one when the user changes the date range quickly.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit be88348. Configure here.

upload: "/employer/documents?add=1",
dashboard: "/employer/home",
analytics: "/employer/documents?feature=analytics",
analytics: "/employer/documents?feature=usage",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Legacy analytics redirect skips Overview

Low Severity

The old analytics view now opens the Usage tab with no at=/overview. ?feature=analytics, #analytics, and /employer/statistics all force Overview, but this redirect leaves the tab on whatever screen it last showed.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit be88348. Configure here.

@Deodat-Lawson
Deodat-Lawson merged commit 6fcd34d into main Nov 1, 2026
12 of 14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant