Repository navigation
feat(usage): Cursor-style Usage tab for the workspace, each person and you - #574
Conversation
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>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 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; |
There was a problem hiding this comment.
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 👍 / 👎.
| const page = await fetchPage(rows.length, PAGE); | ||
| setMore(prev => [...prev, ...page.rows]); |
There was a problem hiding this comment.
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 👍 / 👎.
| 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; | ||
| }); |
There was a problem hiding this comment.
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 👍 / 👎.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.
❌ 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, | ||
| }; | ||
| } |
There was a problem hiding this comment.
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)
Reviewed by Cursor Bugbot for commit be88348. Configure here.
| return; | ||
| } | ||
| setData((await res.json()) as T); | ||
| setFailed(null); |
There was a problem hiding this comment.
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.
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", |
There was a problem hiding this comment.
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.
Reviewed by Cursor Bugbot for commit be88348. Configure here.


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.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
#usage…hashes and?feature=workspace&at=/usageredirect there. The "raise your limit" copy in chat and in error messages now points to Usage › Spending.?feature=analytics,#analyticsand/employer/statisticsopen the Overview.StatisticsViewand/api/company/analysis-dashboardare deleted.How it's built
/api/usage/{summary,people,activity,documents,me}, backed bysrc/server/usage.ai_usage_daily, Ask turns inworkspace_session_messages,workspace_activityanddocument_views.summary,people,activityanddocumentsrequireanalytics.view.meanswers any member with their own numbers only.src/lib/usage/{types,range,csv}.ts.Verification
next buildpassed before the merge with main, which was clean; CI builds the merged branch.tscand eslint are clean.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 newserver/usageaggregation 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=analyticsnow open Usage Overview. Workspace › Usage moves to Usage Spending; settings hashes and legacy workspace/usagepaths redirect there, and AI limit copy points to Usage › Spending.Access: workspace-wide routes stay gated on
analytics.view;/api/usage/meis 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.