Skip to content

feat(ui): show item counts on inbox filter tabs#1922

Open
bluzername wants to merge 2 commits intopaperclipai:masterfrom
bluzername:fix/inbox-tab-count-badges
Open

feat(ui): show item counts on inbox filter tabs#1922
bluzername wants to merge 2 commits intopaperclipai:masterfrom
bluzername:fix/inbox-tab-count-badges

Conversation

@bluzername
Copy link
Copy Markdown

@bluzername bluzername commented Mar 27, 2026

Thinking Path

  • Paperclip orchestrates ai-agents for zero-human companies
  • Human users oversee agents through a web UI, and the Inbox is the main page they land on
  • The Inbox has filter tabs (Mine, Recent, Unread, All) but they did not show how many items each filter has
  • Other pages like Approvals and Agents already show counts on their tabs
  • Without counts, users must click each tab to know how much work is waiting
  • This PR adds issue counts to the three main tabs so users can see at a glance how busy each category is
  • The counts intentionally reflect issues only (not approvals/failed runs/join requests), because issues are the primary work unit that users track and triage in the Inbox

Problem

The Inbox page have four filter tabs at the top: "Mine", "Recent", "Unread", "All". But none of them show how many items are in each category. When I open the inbox, I don't know if I have 3 unread items or 30 without clicking the "Unread" tab first.

The Approvals page already show count on the "Pending" tab (line 90-96 in Approvals.tsx), and we just added counts to the Agents page tabs too. But Inbox was missing this even though it is probably the most important page to have quick count visibility.

What I changed

Added item counts to the three main tabs:

  • Mine (5) - shows mineIssues.length (issues assigned to current user)
  • Recent (12) - shows touchedIssues.length (recently touched issues)
  • Unread (3) - shows unreadTouchedIssues.length (issues with unread activity)
  • All - no count (this tab show mixed content types so a single number would be confusing)

All three arrays are already computed and memoized in the component, so this add zero extra API calls or computation.

Why counts show issues only, not total rendered items

Each tab also render approvals, failed runs, and join requests alongside issues. The counts here intentionally reflect issues only because:

  1. Issues are the primary work unit users care about in the Inbox
  2. Approvals and failed runs are supplementary items that appear on multiple tabs with the same values, so counting them would inflate numbers without giving useful signal
  3. This is consistent with how users think about their inbox - "I have 5 issues to look at" is more useful than "I have 5 issues + 3 approvals that also show on every other tab"

Loading state

Counts are hidden while the queries are still in-flight, so users will not see misleading (0) values. Once data arrive, the count appears next to each tab label.

How to test

  1. Go to Inbox page
  2. While data loads, tabs should show just "Mine", "Recent", "Unread" without any number
  3. After loading, tabs should show counts like "Mine (5)", "Recent (12)", "Unread (3)"
  4. The numbers should match the actual issue items when you click each tab
  5. Non-issue items (approvals, failed runs) are visible in the list but not included in the tab count - this is intentional

1 file, ~7 lines changed.

The Inbox page had four tabs (Mine, Recent, Unread, All) but
none of them show how many items are in each category. User
had to click each tab to find out if there are any items.

Now the first three tabs show counts in parentheses:
- Mine (5)
- Recent (12)
- Unread (3)
- All (no count, shows everything)

The "All" tab doesn't show a count because it include mixed
content types (issues, approvals, join requests, failed runs)
and the total would be misleading.

The counts come from the already-computed memoized arrays:
mineIssues, touchedIssues, unreadTouchedIssues.
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 27, 2026

Greptile Summary

This PR adds item counts to the "Mine", "Recent", and "Unread" inbox filter tabs (e.g. Mine (5), Recent (12), Unread (3)) by reusing three already-memoized arrays, adding zero extra API calls or computation. The change is small and fits naturally alongside similar count patterns on the Approvals and Agents pages.\n\nTwo things worth reviewing before merge:\n\n- Count accuracy: Each tab's workItemsToRender includes approvals, failed runs, and join requests in addition to issues. The new labels only count the issues array (mineIssues.length, touchedIssues.length, unreadTouchedIssues.length), so the displayed count can be lower than the number of items a user actually sees when they click a tab.\n- Loading flash: All three counts render as (0) while the relevant queries are in-flight, since the query defaults are [] and the tab bar is always rendered before allLoaded is true.\n\nAdditionally, per CONTRIBUTING.md, this PR is missing a "thinking path" at the top of the description, and as a visual UI change it should include before/after screenshots. Please add both to the PR description.

Confidence Score: 5/5

Safe to merge; no runtime errors or data issues introduced — remaining findings are UX polish and a PR description gap

All findings are P2: a brief (0) flash during loading and a potential count/rendered-item discrepancy when approvals or failed runs are present. Neither blocks correct functionality. The core change — surfacing issue counts on inbox tabs — is correct and matches how existing arrays are already used.

ui/src/pages/Inbox.tsx — verify whether issue-only counts are the intended design or if total workItemsToRender count would be more accurate

Important Files Changed

Filename Overview
ui/src/pages/Inbox.tsx Adds issue-counts to the "Mine", "Recent", and "Unread" tab labels; counts are already-memoized arrays so no new API calls, but counts show (0) during loading and don't reflect non-issue items (approvals, failed runs, join requests) also rendered in each tab
Prompt To Fix All With AI
This is a comment left during a code review.
Path: ui/src/pages/Inbox.tsx
Line: 963-969

Comment:
**Counts show (0) during loading**

`mineIssuesRaw` and `touchedIssuesRaw` both default to `[]` (lines 586 and 599), so all three counts render as `Mine (0)`, `Recent (0)`, `Unread (0)` while the queries are in-flight. The tab bar is always rendered before `allLoaded` is true, so users will briefly see zeros before the real numbers appear. Consider suppressing the count suffix while loading, e.g.:

```suggestion
                  label: `Mine${isMineIssuesLoading ? "" : ` (${mineIssues.length})`}`,
```
```suggestion
                  label: `Recent${isTouchedIssuesLoading ? "" : ` (${touchedIssues.length})`}`,
```
```suggestion
                { value: "unread", label: `Unread${isTouchedIssuesLoading ? "" : ` (${unreadTouchedIssues.length})`}` },
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: ui/src/pages/Inbox.tsx
Line: 963-969

Comment:
**Tab counts only reflect issues, not all rendered items**

Each tab actually renders a mixed list via `workItemsToRender`, which includes **approvals**, **failed runs**, and **join requests** alongside issues (`getInboxWorkItems` in `inbox.ts` line 174-200). Specifically, from `getApprovalsForTab` (line 156):

- `mine` and `recent` tabs receive **all** approvals
- `unread` tab receives all approvals with actionable statuses

So "Mine (5)" could show 5 issues + several approvals + failed runs, meaning the label count does not match the number of items a user actually sees when they click the tab. If the intent is to count only issues, a comment explaining that design decision would help future readers. If the intent is to reflect total visible items, `workItemsToRender.length` would be the accurate value to use here.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "feat(ui): show item counts on inbox filt..." | Re-trigger Greptile

Comment on lines +963 to +969
label: `Mine (${mineIssues.length})`,
},
{
value: "recent",
label: "Recent",
label: `Recent (${touchedIssues.length})`,
},
{ value: "unread", label: "Unread" },
{ value: "unread", label: `Unread (${unreadTouchedIssues.length})` },
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Counts show (0) during loading

mineIssuesRaw and touchedIssuesRaw both default to [] (lines 586 and 599), so all three counts render as Mine (0), Recent (0), Unread (0) while the queries are in-flight. The tab bar is always rendered before allLoaded is true, so users will briefly see zeros before the real numbers appear. Consider suppressing the count suffix while loading, e.g.:

Suggested change
label: `Mine (${mineIssues.length})`,
},
{
value: "recent",
label: "Recent",
label: `Recent (${touchedIssues.length})`,
},
{ value: "unread", label: "Unread" },
{ value: "unread", label: `Unread (${unreadTouchedIssues.length})` },
label: `Mine${isMineIssuesLoading ? "" : ` (${mineIssues.length})`}`,
Suggested change
label: `Mine (${mineIssues.length})`,
},
{
value: "recent",
label: "Recent",
label: `Recent (${touchedIssues.length})`,
},
{ value: "unread", label: "Unread" },
{ value: "unread", label: `Unread (${unreadTouchedIssues.length})` },
label: `Recent${isTouchedIssuesLoading ? "" : ` (${touchedIssues.length})`}`,
Suggested change
label: `Mine (${mineIssues.length})`,
},
{
value: "recent",
label: "Recent",
label: `Recent (${touchedIssues.length})`,
},
{ value: "unread", label: "Unread" },
{ value: "unread", label: `Unread (${unreadTouchedIssues.length})` },
{ value: "unread", label: `Unread${isTouchedIssuesLoading ? "" : ` (${unreadTouchedIssues.length})`}` },
Prompt To Fix With AI
This is a comment left during a code review.
Path: ui/src/pages/Inbox.tsx
Line: 963-969

Comment:
**Counts show (0) during loading**

`mineIssuesRaw` and `touchedIssuesRaw` both default to `[]` (lines 586 and 599), so all three counts render as `Mine (0)`, `Recent (0)`, `Unread (0)` while the queries are in-flight. The tab bar is always rendered before `allLoaded` is true, so users will briefly see zeros before the real numbers appear. Consider suppressing the count suffix while loading, e.g.:

```suggestion
                  label: `Mine${isMineIssuesLoading ? "" : ` (${mineIssues.length})`}`,
```
```suggestion
                  label: `Recent${isTouchedIssuesLoading ? "" : ` (${touchedIssues.length})`}`,
```
```suggestion
                { value: "unread", label: `Unread${isTouchedIssuesLoading ? "" : ` (${unreadTouchedIssues.length})`}` },
```

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed. Now the count is hidden while query is loading, so no more (0) flash. Pushed in 139c62f.

Comment on lines +963 to +969
label: `Mine (${mineIssues.length})`,
},
{
value: "recent",
label: "Recent",
label: `Recent (${touchedIssues.length})`,
},
{ value: "unread", label: "Unread" },
{ value: "unread", label: `Unread (${unreadTouchedIssues.length})` },
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Tab counts only reflect issues, not all rendered items

Each tab actually renders a mixed list via workItemsToRender, which includes approvals, failed runs, and join requests alongside issues (getInboxWorkItems in inbox.ts line 174-200). Specifically, from getApprovalsForTab (line 156):

  • mine and recent tabs receive all approvals
  • unread tab receives all approvals with actionable statuses

So "Mine (5)" could show 5 issues + several approvals + failed runs, meaning the label count does not match the number of items a user actually sees when they click the tab. If the intent is to count only issues, a comment explaining that design decision would help future readers. If the intent is to reflect total visible items, workItemsToRender.length would be the accurate value to use here.

Prompt To Fix With AI
This is a comment left during a code review.
Path: ui/src/pages/Inbox.tsx
Line: 963-969

Comment:
**Tab counts only reflect issues, not all rendered items**

Each tab actually renders a mixed list via `workItemsToRender`, which includes **approvals**, **failed runs**, and **join requests** alongside issues (`getInboxWorkItems` in `inbox.ts` line 174-200). Specifically, from `getApprovalsForTab` (line 156):

- `mine` and `recent` tabs receive **all** approvals
- `unread` tab receives all approvals with actionable statuses

So "Mine (5)" could show 5 issues + several approvals + failed runs, meaning the label count does not match the number of items a user actually sees when they click the tab. If the intent is to count only issues, a comment explaining that design decision would help future readers. If the intent is to reflect total visible items, `workItemsToRender.length` would be the accurate value to use here.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good catch. The counts are intentionally showing issues only, not total rendered items. I added explanation in the PR description for why - basically issues are the main work unit, and approvals/failed runs show on multiple tabs so counting them would inflate numbers without useful signal. Added a code comment too for future readers.

Suppress count display during query loading so users do not see
misleading (0) values before the real numbers arrive.
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