Skip to content

Data-grid overhaul + session-replays / team-payments dashboard surfaces#1424

Open
mantrakp04 wants to merge 18 commits into
devfrom
refactor/data-grid-and-dashboard-surfaces
Open

Data-grid overhaul + session-replays / team-payments dashboard surfaces#1424
mantrakp04 wants to merge 18 commits into
devfrom
refactor/data-grid-and-dashboard-surfaces

Conversation

@mantrakp04
Copy link
Copy Markdown
Collaborator

@mantrakp04 mantrakp04 commented May 8, 2026

Summary

Refactors the dashboard data-grid into a smaller, URL-state-aware primitive and lands several new dashboard surfaces around it: per-user session replays, team-level analytics and payments, and pagination for permission definitions. Also moves session replays out from under /analytics to a top-level surface and adds a project_user.last_active_at index that the new weekly-active metrics depend on.

Base: devHead: refactor/data-grid-and-dashboard-surfaces
Scope: 91 files, +5,644 / −1,858. Assets in this gist.

Screenshots

Captured from a local dev server (dashboard at :8101, dummy project seeded with 26 users). Standard viewport 1920×1200, widescreen 2560×1440.

Users list — data-grid overhaul in context

Light Dark
users-list-light users-list-dark

Widescreen:

Light Dark
users-list-light-wide users-list-dark-wide

User detail — new session-replays card + weekly metrics

Light Dark
user-detail-light user-detail-dark

Widescreen:

Light Dark
user-detail-light-wide user-detail-dark-wide

Session replays — moved out of /analytics

Light Dark
session-replays-light session-replays-dark

Widescreen:

Light Dark
session-replays-light-wide session-replays-dark-wide

Project permissions — new pagination

Light Dark
project-permissions-light project-permissions-dark

Widescreen:

Light Dark
project-permissions-light-wide project-permissions-dark-wide

Other migrated surfaces

Page Light Dark
Project picker projects-light projects-dark
Overview / setup overview-light overview-dark
Teams list teams-list-light teams-list-dark
Team permissions team-permissions-light team-permissions-dark
API keys api-keys-light api-keys-dark

Scroll behaviour — new data-grid on the users list

Light Dark
users-list-scroll-light users-list-scroll-dark

What's new

  • packages/dashboard-ui-components/src/components/data-grid — rewritten. Trimmed data-grid.tsx from ~1.7k LOC, split sizing logic into data-grid-sizing.ts, added use-url-state.ts for URL-synced state, and added data-grid.test.tsx.
  • Session replays moved from …/analytics/replays to …/session-replays (top-level surface). New user-session-replays.tsx card on the user detail page; new internal route.tsx to feed it.
  • Teams detail page gains team-analytics.tsx and team-payments.tsx.
  • Permissions — new shared permission-definitions-pagination.ts consumed by both project and team permission CRUD routes.
  • Backend — Prisma migration add_project_user_last_active_at_idx + a lastActiveAt index that backs the new weekly-active metrics.
  • Polisheditable-input, inline-save-discard, settings.tsx, walkthrough steps, and several data-table components touched in line with the data-grid rewrite.

Notes for reviewers

  • The data-grid rewrite changes the shape of state (now URL-synced), not just internals. Consumers in apps/dashboard/src/components/data-table/* were updated to match — please scan those for any missed knobs.
  • The analytics/replayssession-replays rename is git-tracked as renames; diffs should be small in those files.
  • New SDK surface in packages/template/src/lib/stack-app/session-replays/index.ts and additions in admin-app-impl.ts / server-app-impl.ts mean OpenAPI specs (docs-mintlify/openapi/{admin,client}.json) regenerate; the diff is mostly mechanical.

Test plan

  • pnpm typecheck clean
  • pnpm lint clean
  • Data-grid unit tests pass (packages/dashboard-ui-components)
  • Manual: users list — column resize, sort, filter, paginate; URL state reflects each change and survives reload
  • Manual: user detail — session-replays card lists replays; weekly-metrics card renders without lastActiveAt index migration applied (i.e. on a fresh DB) and after applying it
  • Manual: project + team permissions — pagination cursor advances and stays consistent under search
  • Manual: session-replays top-level page loads; old /analytics/replays/... URL path is no longer expected to be linked anywhere

Summary by CodeRabbit

  • New Features

    • Session Replays app (embedded mode, search, sorting, share links)
    • Tabbed Team pages with Team Analytics and Team Payments dashboards
    • Server-backed cursor pagination, debounced search, and infinite-scroll for teams/users/permissions
  • UX

    • Permission and member tables refresh after edits; permission creation triggers table refresh
    • Users list supports sorting by last-active
  • Performance

    • Index added to speed ProjectUser last-active queries
  • Documentation

    • API/SDK docs updated for pagination and new query params
    • Contributor guidance: explicit git-safety rules added (no destructive git ops without consent)
  • Tests

    • Added e2e tests for pagination and filtering on list endpoints

…ents surfaces

- Rewrite data-grid with URL-synced state, new sizing logic, tests
- Move analytics/replays → session-replays; add per-user session replays card
- Add team-analytics and team-payments to team detail page
- Add project_user.last_active_at index + permission-definitions pagination
- Various editable-input / inline-save-discard / settings polish
@vercel
Copy link
Copy Markdown

vercel Bot commented May 8, 2026

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

Project Deployment Actions Updated (UTC)
stack-auth-hosted-components Ready Ready Preview, Comment May 12, 2026 9:03pm
stack-auth-mcp Ready Ready Preview, Comment May 12, 2026 9:03pm
stack-backend Ready Ready Preview, Comment May 12, 2026 9:03pm
stack-dashboard Ready Ready Preview, Comment May 12, 2026 9:03pm
stack-demo Ready Ready Preview, Comment May 12, 2026 9:03pm
stack-docs Ready Ready Preview, Comment May 12, 2026 9:03pm
stack-preview-backend Ready Ready Preview, Comment May 12, 2026 9:03pm
stack-preview-dashboard Ready Ready Preview, Comment May 12, 2026 9:03pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 8, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds backend pagination/search and session-replays sorting, extends shared interfaces/SDK and template/server implementations for paginated listings, refactors DataGrid with URL-state and TanStack, converts tables to server-backed infinite sources, adds team analytics/payments and user session-replays UI, updates OpenAPI/docs/registry, adds a DB index, E2E tests, dependency, and AGENTS git-safety guidance.

Changes

Main change DAG

Layer / File(s) Summary
DB Schema / Migration
apps/backend/prisma/*
Adds composite index on ProjectUser(tenancyId,isAnonymous,lastActiveAt) and migration creating it concurrently.
Backend: Permission Pagination
apps/backend/src/app/api/latest/permission-definitions-pagination.ts, project-permission-definitions/*, team-permission-definitions/*
Adds permission-definitions pagination schema/logic and wires list handlers to return cursor-based paginated results.
Backend: Teams/Users/Session-Replays
apps/backend/src/app/api/latest/teams/crud.tsx, users/crud.tsx, internal/session-replays/route.tsx
Teams list: order_by/desc/limit/cursor/query + cursor pagination; Users list: allow order_by last_active_at; Session-replays: accept sort_direction + q, escape ILIKE patterns, validate cursor row against active filters, flip cursor/ORDER BY by direction.
Shared Interfaces & SDK Types
packages/stack-shared/*, packages/template/src/lib/stack-app/*
Adds paginated listing methods/types for teams/users and permission definitions; extends session-replays options with sort/query; updates StackServerApp/StackAdminApp interfaces and app registry.
Template implementations & server caching
packages/template/src/lib/stack-app/apps/implementations/*
Implements paginated methods, maps paginated interface results, forwards replay query/sort, and reworks server-app caches with paginated caching and invalidation.
OpenAPI / Docs / Registry
docs-mintlify/*, docs/content/*, docs/src/components/*, apps/dashboard/src/lib/apps-frontend.tsx, packages/stack-shared/src/apps/apps-config.ts
OpenAPI updated with new query params and stricter enums/integers; SDK/docs updated for paginated APIs; adds session-replays app entry, icon, and walkthrough step rename.
E2E & Tooling
apps/e2e/tests/backend/endpoints/*, turbo.json
Adds teams and permission-definitions pagination e2e tests and a persistent dev:tui turbo task.
Repository guidance
AGENTS.md
Adds git-safety guidance forbidding destructive/working-tree-mutating git operations without explicit user permission.

UI / DataGrid and Tables

Layer / File(s) Summary
DataGrid Core & Dependency
packages/dashboard-ui-components/src/components/data-grid/*, packages/dashboard-ui-components/package.json
Refactors DataGrid to use TanStack Table (selection, sorting, resizing), CSS-variable sizing, ResizeObserver layout tracking, CSV export confirmation; adds @tanstack/react-table dependency.
Sizing & URL-state
packages/dashboard-ui-components/src/components/data-grid/data-grid-sizing.ts, use-url-state.ts, use-url-state.test.tsx
Simplifies sizing helpers, exports DEFAULT_COL_WIDTH/MAX, implements useDataGridUrlState to persist column widths/visibility to URL with parse/serialize and popstate handling (with tests).
Barrel exports & tests
packages/dashboard-ui-components/src/components/data-grid/index.ts, data-grid.test.tsx
Adds toolbar and URL-state hook to exports, expands sizing helpers, narrows some state re-exports, and adds horizontal-scrolling test coverage.
Tables → server-backed
apps/dashboard/src/components/data-table/*, export-users-dialog.tsx
PermissionTable, TeamTable, TeamMemberTable, UserTable, and search tables converted to server-backed infinite/generator data sources, debounced search, refetch contexts, updated columns/sorting, and export adjusted to paginated APIs.
Minor UI tweaks
various apps/dashboard/src/components/*
Styling/spacing tweaks: editable-input ghost styles, SettingCard border, inline-save-discard margin, walkthrough step rename, icon mapping, import reorderings.

Pages / Features

Layer / File(s) Summary
Team pages
apps/dashboard/src/app/.../teams/[teamId]/**
New tabbed TeamPage with TeamHeader; Members/Payments/Analytics/Metadata tabs; adds TeamAnalyticsSection (ClickHouse queries) and TeamPaymentsSection.
User pages / analytics / session replays
apps/dashboard/src/app/.../users/[userId]/*, session-replays/*
User page: URL-driven tabs, activity sidebar, day-filter analytics, paginated recent events, metric card deltas/sparklines, UserSessionReplaysSection; session-replays page: embedded lockedUserId mode and related UI adjustments.
Page wiring updates
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/**
Pages updated to use paginated hooks, table versioning for refresh, pass version into PermissionTable, and minor layout changes.
Component API changes
PermissionTable, TeamMemberTable, TeamTable, UserTable
Several component signatures changed to server-backed patterns (e.g., PermissionTable now accepts permissionType/version; TeamTable no longer accepts teams prop).

Estimated code review effort:
🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested reviewers

  • aadesh18
  • N2D4
  • nams1570

Poem

I hop through patches, tail a-flutter,
Cursors find their rightful gutter.
Grids reborn and replays sing,
Docs align and tests take wing.
A little carrot for this merge! 🐇✨

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/data-grid-and-dashboard-surfaces

…sion handling

- Introduced pagination for teams in the dashboard, allowing for efficient data retrieval and display.
- Updated permission definitions to include cursor-based pagination, improving user experience when navigating through permissions.
- Refactored various components to utilize the new paginated API, ensuring consistency across the application.
- Added error handling for pagination and improved loading states in user session replays and team member tables.
- Enhanced the data grid to support URL-synced state for column widths and visibility, improving user customization options.

This update significantly enhances the dashboard's performance and usability, particularly for users managing large teams and permissions.
- Updated the ProjectUser model to simplify indexing on lastActiveAt.
- Refactored permission definitions pagination to utilize a unified schema, improving consistency across project and team permissions.
- Enhanced error handling in pagination logic for better user feedback.
- Improved loading states and data retrieval efficiency in various components, including session replays and team permissions.

These changes streamline the permission management experience and optimize data handling in the dashboard.
…election logic

- Renamed cursor comparator variable for clarity in session replay queries.
- Enhanced search query handling to include escape character for better SQL compatibility.
- Introduced error handling for asynchronous queries in the Team Analytics section, providing default values for failed queries.
- Refactored data grid selection logic to support flexible row selection modes and improved column width distribution.

These changes enhance the robustness and usability of the dashboard components, particularly in data retrieval and user interaction.
@mantrakp04 mantrakp04 marked this pull request as ready for review May 9, 2026 00:02
Copilot AI review requested due to automatic review settings May 9, 2026 00:02
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/[userId]/user-analytics.tsx (1)

669-691: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Base the chart empty state on the chart window, not the summary window.

When a day filter is active, the chart is built from the expanded daily range (±15 days), but hasAnyEvent still comes from summary.total_events, which only counts the selected day. Filtering to a zero-event day will therefore hide surrounding activity and show the global “No events recorded” empty state instead.

🤖 Prompt for 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.

In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/users/[userId]/user-analytics.tsx
around lines 669 - 691, The empty-state check currently uses
data.summary.total_events which reflects only the summary window; change it to
derive from the chart window data (the densified daily series) so the empty
state matches the displayed chart. Move or recreate hasAnyEvent after the dense
computation and set it to something like: hasAnyEvent = dense.some(d =>
(d.total_events ?? 0) > 0) (or sum dense totals > 0), ensuring the variable
depends on dense (and dayFilter) rather than data.summary.total_events so the UI
reflects densifyDaily/daily and dayFilter logic.
🤖 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
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/users/[userId]/user-analytics.tsx:
- Around line 433-439: The page currently treats any non-empty dayFilter string
as a “filtered” state even when parseDayFilterRange(dayFilter) returns null; fix
by computing a single validated activeDayFilter/validatedFilterRange (call
parseDayFilterRange once, e.g. const activeDayFilter =
parseDayFilterRange(dayFilter)) and use that value everywhere you currently
check dayFilter or filterRange for rendering and querying (banner,
deltas/sparklines, description rewrite); if parseDayFilterRange returns null
clear/reset the URL param or treat the page as unfiltered so the UI and queries
stay consistent (update all usages of filterRange/dayFilter in
user-analytics.tsx including the sections around the parseDayFilterRange call
and the other affected blocks).
- Around line 753-755: The ActivityChart is being hidden at extra-large
viewports because it's wrapped in a div with the "xl:hidden" utility; to fix,
remove or change that responsive class so the chart remains visible on xl
screens—locate the wrapper around the ActivityChart (the div containing
<ActivityChart daily={dense} hasAnyEvent={hasAnyEvent}
description={chartDescription} />) and either delete the "xl:hidden" class or
replace it with the correct responsive utility (e.g., no hide or "hidden
xl:block" if you need it hidden on small screens but shown on xl).

---

Outside diff comments:
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/users/[userId]/user-analytics.tsx:
- Around line 669-691: The empty-state check currently uses
data.summary.total_events which reflects only the summary window; change it to
derive from the chart window data (the densified daily series) so the empty
state matches the displayed chart. Move or recreate hasAnyEvent after the dense
computation and set it to something like: hasAnyEvent = dense.some(d =>
(d.total_events ?? 0) > 0) (or sum dense totals > 0), ensuring the variable
depends on dense (and dayFilter) rather than data.summary.total_events so the UI
reflects densifyDaily/daily and dayFilter logic.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0dfc9c9f-afbf-417c-9090-dab32256f568

📥 Commits

Reviewing files that changed from the base of the PR and between bc9b1e5 and 37ead67.

📒 Files selected for processing (6)
  • apps/backend/src/app/api/latest/permission-definitions-pagination.ts
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/[userId]/user-analytics.tsx
  • apps/dashboard/src/components/data-table/team-member-table.tsx
  • apps/dashboard/src/lib/apps-frontend.tsx
  • docs/content/docs/sdk/types/user.mdx
  • packages/template/src/dev-tool/index.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/backend/src/app/api/latest/permission-definitions-pagination.ts
  • apps/dashboard/src/lib/apps-frontend.tsx
  • packages/template/src/dev-tool/index.ts
  • docs/content/docs/sdk/types/user.mdx
  • apps/dashboard/src/components/data-table/team-member-table.tsx

Comment on lines +433 to +439
function parseDayFilterRange(dayFilter: string): { since: Date, until: Date } | null {
const parts = dayFilter.split("-").map(Number);
if (parts.length !== 3 || parts.some((p) => !Number.isFinite(p))) return null;
const since = new Date(Date.UTC(parts[0], parts[1] - 1, parts[2]));
const until = new Date(since);
until.setUTCDate(until.getUTCDate() + 1);
return { since, until };
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 | 🟠 Major | ⚡ Quick win

Validate dayFilter before treating the page as filtered.

Right now malformed values fall back to the unfiltered queries (filterRange === null), but the render path still shows the filter banner, disables deltas/sparklines, and rewrites descriptions whenever dayFilter is a non-empty string. A bad URL state will therefore show filtered UI around unfiltered analytics. Please derive one validated activeDayFilter and use that for both querying and rendering, or clear invalid values immediately.

Also applies to: 448-451, 586-599

🤖 Prompt for 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.

In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/users/[userId]/user-analytics.tsx
around lines 433 - 439, The page currently treats any non-empty dayFilter string
as a “filtered” state even when parseDayFilterRange(dayFilter) returns null; fix
by computing a single validated activeDayFilter/validatedFilterRange (call
parseDayFilterRange once, e.g. const activeDayFilter =
parseDayFilterRange(dayFilter)) and use that value everywhere you currently
check dayFilter or filterRange for rendering and querying (banner,
deltas/sparklines, description rewrite); if parseDayFilterRange returns null
clear/reset the URL param or treat the page as unfiltered so the UI and queries
stay consistent (update all usages of filterRange/dayFilter in
user-analytics.tsx including the sections around the parseDayFilterRange call
and the other affected blocks).

Comment on lines +753 to +755
<div className="xl:hidden">
<ActivityChart daily={dense} hasAnyEvent={hasAnyEvent} description={chartDescription} />
</div>
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 | 🟠 Major | ⚡ Quick win

The daily activity chart disappears on xl screens.

This is the only ActivityChart render in the component, so wrapping it in xl:hidden removes the chart entirely for extra-large viewports.

♻️ Minimal fix
-      <div className="xl:hidden">
-        <ActivityChart daily={dense} hasAnyEvent={hasAnyEvent} description={chartDescription} />
-      </div>
+      <ActivityChart daily={dense} hasAnyEvent={hasAnyEvent} description={chartDescription} />
🤖 Prompt for 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.

In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/users/[userId]/user-analytics.tsx
around lines 753 - 755, The ActivityChart is being hidden at extra-large
viewports because it's wrapped in a div with the "xl:hidden" utility; to fix,
remove or change that responsive class so the chart remains visible on xl
screens—locate the wrapper around the ActivityChart (the div containing
<ActivityChart daily={dense} hasAnyEvent={hasAnyEvent}
description={chartDescription} />) and either delete the "xl:hidden" class or
replace it with the correct responsive utility (e.g., no hide or "hidden
xl:block" if you need it hidden on small screens but shown on xl).

- Replaced the use of `createDefaultDataGridState` with `useDataGridUrlState` across multiple components to improve state persistence and URL synchronization.
- Updated pagination logic in various tables to ensure consistent handling of grid states and improve user experience during data retrieval.
- Refactored components to utilize the new user picker table for better user selection functionality.

These changes enhance the overall reliability and usability of the dashboard's data grid features.
Comment thread apps/dashboard/src/lib/apps-frontend.tsx Outdated
Comment thread apps/dashboard/src/lib/apps-frontend.tsx Outdated
Comment thread packages/template/src/lib/stack-app/apps/interfaces/admin-app.ts Outdated
Comment thread packages/template/src/lib/stack-app/apps/interfaces/server-app.ts Outdated
Comment thread packages/template/src/lib/stack-app/session-replays/index.ts Outdated
Comment thread packages/template/src/lib/stack-app/teams/index.ts Outdated
Comment thread packages/template/src/lib/stack-app/users/index.ts Outdated
Comment thread examples/e-commerce/src/app/edit-shop/page.tsx Outdated
Comment thread packages/stack-shared/src/apps/apps-config.ts Outdated
Comment thread packages/stack-shared/src/apps/apps-config.ts Outdated
Copy link
Copy Markdown
Contributor

@N2D4 N2D4 left a comment

Choose a reason for hiding this comment

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

some comments

- Removed unused parameters from session replay queries, specifically `sort_direction` and `q`, to simplify the API.
- Updated user and team listing functions to utilize non-paginated APIs, enhancing data retrieval efficiency.
- Refactored related components to support the new API structure, ensuring consistency across the application.
- Improved error handling and response structures for better user feedback during data operations.

These changes optimize the performance of session replay and user management features in the dashboard.
- Modified the `limit` parameter in the user query schema to remove the maximum cap description, simplifying the API documentation.
- Updated corresponding OpenAPI specifications in both admin and server JSON files to reflect the change in the `limit` parameter description.

These changes enhance the clarity of the API documentation and improve user understanding of the query parameters.
- Added a newline at the end of the `admin.json` and `server.json` files to comply with JSON formatting standards. This change ensures better compatibility with various parsers and tools that expect a newline at the end of files.
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.

3 participants