Skip to content

fix(chat): focus + height polish; feat(admin): devpass margin% + chart - #2220

Merged
smakosh merged 6 commits into
mainfrom
fix/chat-sidebar-and-devpass-improvements
May 9, 2026
Merged

smakosh merged 6 commits into
mainfrom
fix/chat-sidebar-and-devpass-improvements

Conversation

@smakosh

@smakosh smakosh commented May 9, 2026 •

Copy link
Copy Markdown
Member

Summary

Two related changes bundled into one PR.

1. Chat sidebar virtualization follow-ups (#2215)

  • Stop autoFocus stealing on scroll. Inline rename Input no longer uses autoFocus. Parent state pendingFocusChatId flips on edit-start and is cleared once the input has actually delivered focus, so re-mounting the editing row on scroll never re-steals focus mid-edit.
  • Lock row heights. Hard-coded ROW_HEIGHT_HEADER / ROW_HEIGHT_SPACER / ROW_HEIGHT_CHAT constants used by getChatHistoryRowHeight; row content stretches with h-full! and top-1/2 -translate-y-1/2 so the menu action stays vertically centered regardless of slot height.
  • Memoize rowProps so unrelated parent re-renders don't bust every visible row.
  • Drop unused formatDate prop — already module-level, imported directly.
  • Remove the redundant !isChatsLoading guard inside the empty-state branch (the early return already covers it).

2. DevPass admin dashboard

  • Profit margin % added next to the dollar value in the Cycle margin KPI on /devpass. Formula: totalMargin / totalMrrCycle. Also surfaced per-subscriber on the detail response as marginPct (currently only KPI subtitle uses it; per-row UI can come later).
  • Click-to-copy IDs on the Subscription history and Payment failures tables in /devpass/[orgId], via a new tiny CopyableId client component (sonner toast on success, ✓ flicker confirmation).
  • Historical chart on /devpass: new DevpassTimeseriesChart shows daily revenue, real provider cost, and margin. Uses the same <DateRangePicker /> as the main admin dashboard (presets including 30d, 90d, 365d, All time, plus custom).
  • New API route GET /admin/devpass/timeseries — accepts from/to, defaults to all-time anchored on the earliest dev_plan_start. Revenue: completed dev_plan_* transactions. Cost: project_hourly_stats.cost for projects under orgs that are or were ever on a DevPass plan.

Test plan

  • On / (chat), rename a chat in a long history, scroll the sidebar so the editing row exits the viewport, scroll back — the input stays focused/active and does not re-steal focus.
  • Rename a chat then click another chat — the rename saves correctly (blur path).
  • Press Escape in rename — title resets, no save mutation fired.
  • On /devpass, switch the date picker between 30d / 90d / 365d / All time — chart series and totals update; KPI strip reflects margin %.
  • On /devpass/[orgId], click a row ID in either table — clipboard contains the full id, toast shown.
  • pnpm format clean; pnpm --filter admin tsc --noEmit clean; pnpm --filter admin build succeeds.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • DevPass timeseries analytics endpoint with daily revenue, cost, and margin plus range totals
    • Interactive DevPass chart with metric toggle and date-range filtering
    • Copyable ID buttons added to DevPass admin tables
  • New Metrics

    • Margin percentage (marginPct) surfaced in DevPass subscriber and KPI responses
  • Improvements

    • Improved chat sidebar title editing and focus handling

Review Change Stack

smakosh and others added 2 commits May 9, 2026 11:59
Follow-up to #2215. Replaces autoFocus on the inline rename Input with a
parent-controlled pendingFocusChatId so scroll-induced remounts no
longer re-steal focus mid-edit, memoizes rowProps, hard-codes row
heights through ROW_HEIGHT_* constants and stretches the row content
to fill the slot, and drops the unused formatDate prop.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds marginPct to /admin/devpass list and detail responses. Renders %
margin in the Cycle margin KPI subtitle on /devpass. Introduces
GET /admin/devpass/timeseries (revenue, cost, margin per day) wired into
a new DevpassTimeseriesChart on /devpass with the existing
DateRangePicker (30d, 90d, 365d, all time, custom). Adds an ID column
with click-to-copy on the per-org subscription history and payment
failure tables for easier debugging.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 9, 2026 •

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9da3eaa5-74ca-45b8-9328-9eeabc6f38e2

📥 Commits

Reviewing files that changed from the base of the PR and between 9c4fecf and 635ad9d.

⛔ Files ignored due to path filters (3)
  • apps/code/src/lib/api/v1.d.ts is excluded by !**/v1.d.ts
  • apps/playground/src/lib/api/v1.d.ts is excluded by !**/v1.d.ts
  • apps/ui/src/lib/api/v1.d.ts is excluded by !**/v1.d.ts
📒 Files selected for processing (3)
  • ee/admin/src/app/devpass/page.tsx
  • ee/admin/src/components/copyable-id.tsx
  • ee/admin/src/components/devpass-timeseries-chart.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • ee/admin/src/components/copyable-id.tsx
  • ee/admin/src/components/devpass-timeseries-chart.tsx
  • ee/admin/src/app/devpass/page.tsx

Walkthrough

This PR adds DevPass margin percentage analytics and a new /devpass/timeseries API, integrates a date-range-filtered timeseries chart into admin pages, adds CopyableId UI and ID columns to DevPass detail tables, and refactors the playground chat sidebar edit/focus flow with memoized row props.

Changes

DevPass Analytics and Timeseries

Layer / File(s) Summary
API Schemas and Data Contracts
apps/api/src/routes/admin.ts
Adds nullable marginPct to devpassSubscriberSchema and devpassKpisSchema; introduces devpassTimeseriesPointSchema, devpassTimeseriesSchema, and route definition for GET /devpass/timeseries with optional from/to.
Timeseries Endpoint Implementation
apps/api/src/routes/admin.ts
Implements GET /devpass/timeseries by aggregating daily revenue from completed DevPass transactions and daily cost from project hourly stats for orgs on/ever-on DevPass plans, fills missing dates day-by-day, and returns data, totals, and range.
Margin Percentage Computation
apps/api/src/routes/admin.ts
Computes marginPct for subscriber list/detail responses and KPIs by dividing margin by MRR or total MRR cycle; returns null when denominator is non-positive.
Copyable ID Component
ee/admin/src/components/copyable-id.tsx
New client component rendering a truncating ID button that copies to clipboard on click, shows transient success/error toasts, and swaps icon while copied.
Timeseries Chart Component
ee/admin/src/components/devpass-timeseries-chart.tsx
Client component that fetches timeseries data with optional date range, supports metric selection (revenue,cost,margin), formats totals/tooltips, and renders a Recharts line chart with loading/error/empty states.
Detail Page Table ID Columns
ee/admin/src/app/devpass/[orgId]/page.tsx
Adds an ID column to transaction history and payment failures tables, renders IDs via CopyableId, and adjusts empty-state colSpan.
Main Page Integration
ee/admin/src/app/devpass/page.tsx
Parses optional from/to from searchParams, renders DateRangePicker in header (Suspense), includes hidden form inputs for range propagation, conditionally displays marginPct with sign styling in KPI card, and inserts DevpassTimeseriesChart with range props.

Chat Sidebar Edit Focus Refactoring

Layer / File(s) Summary
React Hooks and Row Constants
apps/playground/src/components/playground/chat-sidebar.tsx
Adds useCallback, useEffect, useRef imports and introduces HEADER_HEIGHT, SPACER_HEIGHT, CHAT_ROW_HEIGHT with updated getChatHistoryRowHeight.
Component Contract Refactoring
apps/playground/src/components/playground/chat-sidebar.tsx
Updates ChatHistoryRowProps: removes formatDate, adds pendingFocusChatId and onEditFocused to enable parent-controlled focus targeting.
EditChatTitleInput Component
apps/playground/src/components/playground/chat-sidebar.tsx
New input component that conditionally focuses/selects, handles Enter (save/blur), Escape (cancel), and blur (save), and notifies parent when focused.
ChatHistoryRowComponent Refactoring
apps/playground/src/components/playground/chat-sidebar.tsx
Uses EditChatTitleInput when editing, derives isEditing from editingId, uses local date formatting, and shows action menu only when current chat and not editing.
ChatSidebar Focus and Memoization
apps/playground/src/components/playground/chat-sidebar.tsx
Adds pendingFocusChatId state, converts handlers to useCallback, memoizes rowProps via useMemo and passes them to List to avoid prop recreation and focus re-stealing during scroll.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • theopenco/llmgateway#2085: Modifies DevPass admin API and KPI/subscriber billing metrics; related to margin/MRR computations.
  • theopenco/llmgateway#2215: Refactors playground chat sidebar rendering and virtualization; related to chat-sidebar edits and focus behavior.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: chat sidebar focus/height fixes and DevPass admin margin% feature with chart visualization.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/chat-sidebar-and-devpass-improvements

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ee/admin/src/app/devpass/page.tsx`:
- Around line 313-314: The parsed from/to query params used by
DevpassTimeseriesChart must be preserved when building any table interactions
(search, sort, filter, pagination): update the URL/query-string builders and
link/navigation functions that construct table requests (the functions that
currently build the search/sort/filter/pagination URLs) to include the existing
from and to values so they are forwarded on every interaction; ensure the
components that call those builders (the table controls and pagination handlers)
read from/to from the current router/query state and merge them into the new
query object so the chart retains the selected date range across actions.

In `@ee/admin/src/components/copyable-id.tsx`:
- Around line 18-27: The setTimeout in handleCopy (inside the CopyableId
component) can update state after unmount; fix by tracking the timeout ID and
clearing it on unmount: import and use useEffect (or useRef) in the CopyableId
component to store the timer id returned from setTimeout in handleCopy, call
clearTimeout(timerId) in a cleanup effect, and ensure you don't call
setCopied(false) after unmount; remove or guard the pending timeout when the
component unmounts to prevent state updates on an unmounted component.

In `@ee/admin/src/components/devpass-timeseries-chart.tsx`:
- Around line 89-97: The segmented-control buttons only indicate selection
visually; update the button element used inside the map (the one with key={key},
onClick={() => setActiveSeries(key)}, and data-active={activeSeries === key}) to
expose state to assistive tech by adding an aria-pressed attribute set to
{activeSeries === key}; ensure the attribute value reflects the same boolean
used for data-active so screen readers know which series is active (reference
activeSeries, setActiveSeries, and the button with key={key}).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8c393c26-ce05-4d35-9d57-52ee4e7d25cf

📥 Commits

Reviewing files that changed from the base of the PR and between b4438d2 and 9c4fecf.

⛔ Files ignored due to path filters (1)
  • ee/admin/src/lib/api/v1.d.ts is excluded by !**/v1.d.ts
📒 Files selected for processing (6)
  • apps/api/src/routes/admin.ts
  • apps/playground/src/components/playground/chat-sidebar.tsx
  • ee/admin/src/app/devpass/[orgId]/page.tsx
  • ee/admin/src/app/devpass/page.tsx
  • ee/admin/src/components/copyable-id.tsx
  • ee/admin/src/components/devpass-timeseries-chart.tsx

Comment on lines +7551 to +7554
query: z.object({
from: z.string().optional(),
to: z.string().optional(),
}),

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Validate from/to together on the new DevPass timeseries route.

The schema accepts arbitrary strings, but the handler only honors the range when both params are present. Right now a partial range silently falls back to all-time, and malformed values can flow into new Date(...)/SQL instead of returning a clean 4xx. Require valid YYYY-MM-DD values and either both params or neither.

Also applies to: 8308-8324

Comment thread ee/admin/src/app/devpass/page.tsx
Comment thread ee/admin/src/components/copyable-id.tsx
Comment thread ee/admin/src/components/devpass-timeseries-chart.tsx
smakosh and others added 4 commits May 9, 2026 12:06
Forward `from`/`to` through the devpass table query-string and form
action so chart range survives sort/filter/page interactions. Cancel
the CopyableId reset timeout on unmount to avoid late state updates.
Add aria-pressed to the timeseries chart's segmented control.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@smakosh smakosh self-assigned this May 9, 2026
@smakosh
smakosh added this pull request to the merge queue May 9, 2026
Merged via the queue into main with commit d77790f May 9, 2026
11 checks passed
@smakosh
smakosh deleted the fix/chat-sidebar-and-devpass-improvements branch May 9, 2026 10:51
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.

2 participants