Skip to content

[Dashboard] Redefine the user page with tabs and updated UI#1351

Merged
madster456 merged 38 commits intodevfrom
dashboard/updated_user_page
May 5, 2026
Merged

[Dashboard] Redefine the user page with tabs and updated UI#1351
madster456 merged 38 commits intodevfrom
dashboard/updated_user_page

Conversation

@madster456
Copy link
Copy Markdown
Collaborator

@madster456 madster456 commented Apr 19, 2026

Summary by CodeRabbit

  • New Features

    • Tabbed user profile with Activity (30-day analytics, KPIs, daily chart, top lists, recent events), Payments (transactions, subscriptions, product/item balances) and an activity heatmap sidebar.
    • New internal user-activity API and admin-facing activity hook; admin API client can fetch per-user activity.
  • UI/UX Improvements

    • Unified menus, cards and tables; inline editable user details with accept/revert; metadata editor validates JSON; country-code input has draft editing; tabs support optional icons.
  • API

    • Transactions endpoint and admin transaction queries now support optional customer-scoped filtering.
  • Tests

    • End-to-end coverage for the user-activity endpoint.
image image image

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 19, 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 5, 2026 4:42pm
stack-backend Ready Ready Preview, Comment May 5, 2026 4:42pm
stack-dashboard Ready Ready Preview, Comment May 5, 2026 4:42pm
stack-demo Ready Ready Preview, Comment May 5, 2026 4:42pm
stack-docs Ready Ready Preview, Comment May 5, 2026 4:42pm
stack-preview-backend Ready Ready Preview, Comment May 5, 2026 4:42pm
stack-preview-dashboard Ready Ready Preview, Comment May 5, 2026 4:42pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 19, 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

Refactors the user page to design-system primitives, adds tabbed layout, client analytics and payments sections, implements a per-user activity heatmap (backend route + client hook), extends transaction APIs with customerId, tightens metadata typing, and adds E2E tests for the activity endpoint.

Changes

Cohort / File(s) Summary
User page refactor
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx
Rebuilds user page with design-system primitives (DesignMenu, DesignCard, DesignDataTable, DesignEditableGrid), DesignCategoryTabs, memoized ColumnDefs, ActionCell, RestrictionDialog, and Suspense-wrapped activity sidebar.
Analytics & Payments UI
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/[userId]/user-analytics.tsx, apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/[userId]/user-payments.tsx
Adds UserAnalyticsSection (30-day queries, densified series, charts/cards/lists) and UserPaymentsSection (transactions, active grants, item balances, metrics).
Backend user-activity route
apps/backend/src/app/api/latest/internal/user-activity/route.tsx
New internal GET route requiring admin auth + user_id, queries ClickHouse for daily event counts over fixed window, returns complete data_points array, maps ClickHouse errors to ServiceUnavailable.
Internals & client hook
apps/dashboard/src/lib/stack-app-internals.ts, packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts, packages/stack-shared/src/interface/admin-interface.ts, packages/stack-shared/src/interface/admin-metrics.ts
Expose/useUserActivity internals and add useUserActivityOrThrow; add getUserActivity client method and UserActivityResponse schema/type; extend transaction APIs to accept and thread optional customerId.
E2E tests
apps/e2e/tests/backend/endpoints/api/v1/internal-user-activity.test.ts
Adds tests for endpoint shape, auth behavior, and eventual non-zero ingestion with polling/timeouts.
Component typing & editors
apps/dashboard/src/components/metadata-editor.tsx, apps/dashboard/src/components/country-code-select.tsx, apps/dashboard/src/components/design-components/editable-grid.tsx, packages/dashboard-ui-components/src/components/tabs.tsx
Tighten metadata props to typed Json and update onUpdate signatures; CountryCodeInput gains draft state; DesignEditableInput adds local edit state, normalizeInput prop and accept/reject controls; tabs support optional SVG icon prop.
Payments route update
apps/backend/src/app/api/latest/internal/payments/transactions/route.tsx
Adds optional customer_id query param and applies it to ledger/refund SQL filters; updates handler schema and call site.
Dashboard UI wiring
apps/dashboard/src/app/.../users/.../*
Converts multiple per-section tables to DesignCard + DesignDataTable, introduces shared boolean-icon cell helper, per-row action items (send email/invite, toggle flags, set primary, delete, View Team), and activity heatmap sidebar with skeletons/tooltips.

Sequence Diagram(s)

sequenceDiagram
    participant AdminUI as Admin Client
    participant Internals as Admin Internals (useUserActivity)
    participant Backend as Backend /internal/user-activity
    participant ClickHouse as ClickHouse (analytics_internal.events)
    participant ActivityUI as ActivityGraph / Suspense

    AdminUI->>Internals: call useUserActivity(userId)
    Internals->>Backend: GET /internal/user-activity?user_id={userId}
    Backend->>ClickHouse: query daily event counts (fixed UTC window)
    ClickHouse-->>Backend: rows per day or error
    Backend-->>Internals: { data_points: [...] }
    Internals-->>AdminUI: UserActivityResponse
    AdminUI->>ActivityUI: render (Suspense -> ActivityGraph)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 I hopped through code and stitched a tab or two,
Cards and charts now paint the user's view.
Heatmaps warm the sidebar, rows politely dance,
Buttons, edits, tests—I've given them a chance.
A tiny rabbit claps: "Deploy—let's prance!"

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The PR description is incomplete and provides no meaningful information about the changes, objectives, or scope of this significant refactoring effort. Add a comprehensive PR description explaining the changes, including the rationale for the user page refactoring, new features added (activity heatmap, category tabs, updated UI components), and any breaking changes or migration notes for reviewers.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title '[Dashboard] Redefine the user page with tabs and updated UI' is directly related to the main changes, which involve restructuring the user page with tabbed navigation and migrating UI components to design-system primitives.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dashboard/updated_user_page

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 19, 2026

Greptile Summary

This PR refactors the user detail page in the dashboard to use the shared DesignCard, DesignDataTable, DesignEditableGrid, DesignMenu, and DesignCategoryTabs design-system components, and introduces a tab-based layout (Profile / Analytics / Payments / Fraud Protection) with placeholder content for the non-profile tabs. The contact channels, teams, and OAuth providers sections are migrated from raw table markup to DesignDataTable column definitions.

  • P1: handleRemoveRestriction has no catch block — any failure is a silent unhandled rejection with no user feedback.
  • P1: handleSaveAndRestrict catches errors only with captureError and never surfaces them to the user, leaving the dialog in a stuck-but-silent state on failure.

Confidence Score: 4/5

Safe to merge after fixing silent error handling in RestrictionDialog; the two P1s leave users with no feedback when restriction operations fail.

Two P1 findings exist: unhandled rejections in handleRemoveRestriction and silent error swallowing in handleSaveAndRestrict. Both affect the restriction management flow. The remaining findings (oauthColumns useMemo, runAsynchronouslyWithAlert on menu items) are P2. Score is 4 pending the P1 fixes.

apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx — specifically the RestrictionDialog handlers and OAuthProvidersSection

Important Files Changed

Filename Overview
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx Large UI refactor migrating tables to DesignDataTable and adding tab layout; two P1 issues: silent error handling in RestrictionDialog and missing useMemo on oauthColumns

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[UserPage] --> B[RestrictionBanner]
    A --> C[UserHeader\nDesignMenu actions]
    A --> D[DesignCategoryTabs]
    D --> E{selectedTab}
    E -->|profile| F[UserDetails\nDesignEditableGrid]
    E -->|profile| G[ContactChannelsSection\nDesignDataTable]
    E -->|profile| H[UserTeamsSection\nDesignDataTable]
    E -->|profile| I[OAuthProvidersSection\nDesignDataTable]
    E -->|profile| J[MetadataSection]
    E -->|analytics| K[TabPlaceholder]
    E -->|payments| L[TabPlaceholder]
    E -->|fraud-protection| M[TabPlaceholder]
    C -->|impersonate / remove-2fa| N[async onClick\n⚠ no runAsynchronouslyWithAlert]
    F --> O[RestrictionDialog]
    O -->|Save & restrict| P[handleSaveAndRestrict\n⚠ silent catch]
    O -->|Remove restriction| Q[handleRemoveRestriction\n⚠ no catch block]
Loading

Comments Outside Diff (2)

  1. apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx, line 212-224 (link)

    P1 Unhandled rejection in handleRemoveRestriction

    There is no catch block, so if user.update() throws the error propagates as an unhandled rejection — the dialog freezes or closes silently with no user-facing feedback. Per the codebase convention, async button handlers should use runAsynchronouslyWithAlert instead of manual try/finally.

    Rule Used: Use runAsynchronouslyWithAlert from `@stackframe... (source)

    Learned From
    stack-auth/stack-auth#943

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx
    Line: 212-224
    
    Comment:
    **Unhandled rejection in `handleRemoveRestriction`**
    
    There is no `catch` block, so if `user.update()` throws the error propagates as an unhandled rejection — the dialog freezes or closes silently with no user-facing feedback. Per the codebase convention, async button handlers should use `runAsynchronouslyWithAlert` instead of manual try/finally.
    
    
    
    **Rule Used:** Use `runAsynchronouslyWithAlert` from `@stackframe... ([source](https://app.greptile.com/review/custom-context?memory=5e671275-7493-402a-93a8-969537ec4d63))
    
    **Learned From**
    [stack-auth/stack-auth#943](https://github.com/stack-auth/stack-auth/pull/943)
    
    How can I resolve this? If you propose a fix, please make it concise.
  2. apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx, line 195-210 (link)

    P1 Silent error in handleSaveAndRestrict — user receives no feedback on failure

    The catch block calls captureError but never shows anything in the UI. When user.update() fails, the dialog stays open with isSaving reset to false but with no toast, alert, or message — the user has no idea what went wrong. The native alert() used for the validation check is also inconsistent with the rest of the codebase (which uses toasts).

    Consider using runAsynchronouslyWithAlert (or at minimum adding a toast in the catch block) so failures are surfaced to the user.

    Rule Used: Use runAsynchronouslyWithAlert from `@stackframe... (source)

    Learned From
    stack-auth/stack-auth#943

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx
    Line: 195-210
    
    Comment:
    **Silent error in `handleSaveAndRestrict` — user receives no feedback on failure**
    
    The `catch` block calls `captureError` but never shows anything in the UI. When `user.update()` fails, the dialog stays open with `isSaving` reset to `false` but with no toast, alert, or message — the user has no idea what went wrong. The native `alert()` used for the validation check is also inconsistent with the rest of the codebase (which uses toasts).
    
    Consider using `runAsynchronouslyWithAlert` (or at minimum adding a toast in the catch block) so failures are surfaced to the user.
    
    **Rule Used:** Use `runAsynchronouslyWithAlert` from `@stackframe... ([source](https://app.greptile.com/review/custom-context?memory=5e671275-7493-402a-93a8-969537ec4d63))
    
    **Learned From**
    [stack-auth/stack-auth#943](https://github.com/stack-auth/stack-auth/pull/943)
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx
Line: 212-224

Comment:
**Unhandled rejection in `handleRemoveRestriction`**

There is no `catch` block, so if `user.update()` throws the error propagates as an unhandled rejection — the dialog freezes or closes silently with no user-facing feedback. Per the codebase convention, async button handlers should use `runAsynchronouslyWithAlert` instead of manual try/finally.

```suggestion
  const handleRemoveRestriction = () => {
    runAsynchronouslyWithAlert(async () => {
      setIsSaving(true);
      try {
        await user.update({
          restrictedByAdmin: false,
          restrictedByAdminReason: null,
          restrictedByAdminPrivateDetails: null,
        } as any);
        onOpenChange(false);
      } finally {
        setIsSaving(false);
      }
    });
  };
```

**Rule Used:** Use `runAsynchronouslyWithAlert` from `@stackframe... ([source](https://app.greptile.com/review/custom-context?memory=5e671275-7493-402a-93a8-969537ec4d63))

**Learned From**
[stack-auth/stack-auth#943](https://github.com/stack-auth/stack-auth/pull/943)

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

---

This is a comment left during a code review.
Path: apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx
Line: 195-210

Comment:
**Silent error in `handleSaveAndRestrict` — user receives no feedback on failure**

The `catch` block calls `captureError` but never shows anything in the UI. When `user.update()` fails, the dialog stays open with `isSaving` reset to `false` but with no toast, alert, or message — the user has no idea what went wrong. The native `alert()` used for the validation check is also inconsistent with the rest of the codebase (which uses toasts).

Consider using `runAsynchronouslyWithAlert` (or at minimum adding a toast in the catch block) so failures are surfaced to the user.

**Rule Used:** Use `runAsynchronouslyWithAlert` from `@stackframe... ([source](https://app.greptile.com/review/custom-context?memory=5e671275-7493-402a-93a8-969537ec4d63))

**Learned From**
[stack-auth/stack-auth#943](https://github.com/stack-auth/stack-auth/pull/943)

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

---

This is a comment left during a code review.
Path: apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx
Line: 1171-1243

Comment:
**`oauthColumns` not wrapped in `useMemo`**

Both `teamColumns` (line 906) and `contactChannelColumns` (line 740) are wrapped in `useMemo`, but `oauthColumns` is a plain `const` inside the component body, so it is recreated on every render. Wrapping it in `useMemo` keeps the pattern consistent and avoids unnecessary downstream re-renders in `DesignDataTable`.

```suggestion
  const oauthColumns = useMemo<ColumnDef<ServerOAuthProvider>[]>(() => [
```

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

---

This is a comment left during a code review.
Path: apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx
Line: 113-131

Comment:
**Async `DesignMenu` `onClick` handlers not wrapped in `runAsynchronouslyWithAlert`**

`DesignMenu` passes `onClick` directly to the underlying `DropdownMenuItem` without any error boundary, so the "Impersonate" and "Remove 2FA" async handlers can throw unhandled rejections with no user-visible feedback. The codebase convention is to wrap async button handlers in `runAsynchronouslyWithAlert`.

```suggestion
            onClick: () => runAsynchronouslyWithAlert(async () => {
              const expiresInMillis = 1000 * 60 * 60 * 2;
              const expiresAtDate = new Date(Date.now() + expiresInMillis);
              const session = await user.createSession({ expiresInMillis });
              const tokens = await session.getTokens();
              setImpersonateSnippet(deindent`
                document.cookie = 'stack-refresh-${stackAdminApp.projectId}=${tokens.refreshToken}; expires=${expiresAtDate.toUTCString()}; path=/'; 
                window.location.reload();
              `);
            }),
```

And similarly for the "Remove 2FA" `onClick`.

**Rule Used:** Use `runAsynchronouslyWithAlert` from `@stackframe... ([source](https://app.greptile.com/review/custom-context?memory=5e671275-7493-402a-93a8-969537ec4d63))

**Learned From**
[stack-auth/stack-auth#943](https://github.com/stack-auth/stack-auth/pull/943)

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

Reviews (1): Last reviewed commit: "fix padding" | Re-trigger Greptile

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: 3

🧹 Nitpick comments (3)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx (3)

125-131: satisfies only covers the truthy branch; move it to assert the whole items array.

satisfies DesignMenuActionItem[] is applied inside the conditional spread, so neither the static items nor the empty-array branch are type-checked against DesignMenuActionItem[]. If you want the assertion to protect the entire list, place it on the outer items array (or type the intermediate conditional as DesignMenuActionItem[] on both branches).

♻️ Proposed shape
-          items={[
+          items={([
             { id: "impersonate", /* ... */ },
-            ...user.isMultiFactorRequired ? [{
-              id: "remove-2fa",
-              /* ... */
-            }] satisfies DesignMenuActionItem[] : [],
+            ...(user.isMultiFactorRequired ? [{
+              id: "remove-2fa",
+              /* ... */
+            }] : []),
             { id: "delete", /* ... */ },
-          ]}
+          ] satisfies DesignMenuActionItem[])}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx
around lines 125 - 131, The conditional currently applies "satisfies
DesignMenuActionItem[]" only to the truthy spread branch (the array with id
"remove-2fa"), so the overall items list (including the [] branch) is not
type-checked; fix this by moving the satisfies assertion to the outer items
array (or explicitly type both branches as DesignMenuActionItem[]), e.g. ensure
the final items array that includes "...user.isMultiFactorRequired ? [{ id:
'remove-2fa', label: 'Remove 2FA', onClick: async () => { await user.update({
totpMultiFactorSecret: null }); }, }] : []" is asserted with "satisfies
DesignMenuActionItem[]" so the whole list (not just the truthy branch) is
validated against DesignMenuActionItem.

1355-1402: Consider persisting the selected tab in the URL.

selectedTab lives only in component state, so refreshing or sharing a link always lands on the Profile tab. For a multi-tab user detail page this is usually undesirable (especially once Analytics/Payments/Fraud-Protection become real content). Consider syncing with a query param (e.g. ?tab=analytics) via useSearchParams + router.replace, or storing in sessionStorage as a minimum.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx
around lines 1355 - 1402, Persist the UserPage selectedTab state to the URL so
tab survives refresh/share: update the UserPage component to initialize
selectedTab from a query param (e.g., ?tab=...) and keep it in sync on changes
by calling setSelectedTab when the query param changes; on tab clicks call
router.replace (or update useSearchParams) to set the tab param instead of only
setSelectedTab, and fall back to USER_PAGE_TABS[0] if the param is invalid;
alternatively, if URL sync is undesirable, persist selectedTab to sessionStorage
on change and restore it on mount; touch symbols: UserPage, selectedTab,
setSelectedTab, USER_PAGE_TABS, useSearchParams/router.replace or sessionStorage
to implement this.

1171-1243: oauthColumns should be memoized like the other column definitions.

contactChannelColumns (line 740) and teamColumns (line 906) are wrapped in useMemo, but oauthColumns is recreated on every render. This produces a new columns reference for DesignDataTable on each render, which typically defeats TanStack Table's memoized row models and can cause visible churn. Please make this consistent.

♻️ Proposed fix
-  const oauthColumns: ColumnDef<ServerOAuthProvider>[] = [
+  const oauthColumns = useMemo<ColumnDef<ServerOAuthProvider>[]>(() => [
     ...
-  ];
+  ], [handleProviderUpdate]);

Note that handleProviderUpdate itself is recreated each render; if you want full stability, wrap it in useCallback or inline it into the memo.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx
around lines 1171 - 1243, The oauthColumns definition is not memoized which
causes a new columns reference each render; wrap the oauthColumns array in
useMemo (like contactChannelColumns and teamColumns) to return a stable
reference for DesignDataTable, e.g. const oauthColumns = useMemo(() => [...],
[/* dependencies */]); and include handleProviderUpdate and any props/state used
inside the column cells in the dependency array (or stabilize
handleProviderUpdate with useCallback) so the memo remains correct and avoids
unnecessary re-renders.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx:
- Around line 939-943: The onClick handler currently builds the URL with string
interpolation using stackAdminApp.projectId and row.original.id; update it to
construct the URL with urlString`...` or by wrapping dynamic parts with
encodeURIComponent() (e.g., encodeURIComponent(stackAdminApp.projectId) and
encodeURIComponent(row.original.id)) before passing to window.open in the
onClick for the "View Team" item so the URL follows the repository guideline for
safe/consistent URL construction.
- Around line 1298-1335: ActivityPlaceholder currently seeds random values
during render inside useMemo using Math.random (cells), causing server/client
hydration mismatches; change it to generate the random cells only after mount by
replacing the useMemo/Math.random usage with a client-only initialization (e.g.,
useState + useEffect or a mounted flag) so cells are populated in an effect
after the component mounts (still referencing ACTIVITY_GRID_WEEKS and
ACTIVITY_GRID_DAYS and keeping the same className grid rendering), ensuring the
initial server HTML matches and preventing React hydration warnings.
- Around line 1366-1373: The code is unsafely casting the onSelect value to
UserPageTab; update the onSelect handler for DesignCategoryTabs to
validate/narrow the incoming id against the known USER_PAGE_TABS array instead
of using "as UserPageTab". Implement a runtime check that confirms id is one of
the entries in USER_PAGE_TABS (e.g., find/includes) and only call
setSelectedTab(id) when it matches; otherwise ignore or handle the unexpected
value (fallback to a default or no-op). Ensure selectedTab and setSelectedTab
continue to use the UserPageTab type so the value stored is type-safe.

---

Nitpick comments:
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx:
- Around line 125-131: The conditional currently applies "satisfies
DesignMenuActionItem[]" only to the truthy spread branch (the array with id
"remove-2fa"), so the overall items list (including the [] branch) is not
type-checked; fix this by moving the satisfies assertion to the outer items
array (or explicitly type both branches as DesignMenuActionItem[]), e.g. ensure
the final items array that includes "...user.isMultiFactorRequired ? [{ id:
'remove-2fa', label: 'Remove 2FA', onClick: async () => { await user.update({
totpMultiFactorSecret: null }); }, }] : []" is asserted with "satisfies
DesignMenuActionItem[]" so the whole list (not just the truthy branch) is
validated against DesignMenuActionItem.
- Around line 1355-1402: Persist the UserPage selectedTab state to the URL so
tab survives refresh/share: update the UserPage component to initialize
selectedTab from a query param (e.g., ?tab=...) and keep it in sync on changes
by calling setSelectedTab when the query param changes; on tab clicks call
router.replace (or update useSearchParams) to set the tab param instead of only
setSelectedTab, and fall back to USER_PAGE_TABS[0] if the param is invalid;
alternatively, if URL sync is undesirable, persist selectedTab to sessionStorage
on change and restore it on mount; touch symbols: UserPage, selectedTab,
setSelectedTab, USER_PAGE_TABS, useSearchParams/router.replace or sessionStorage
to implement this.
- Around line 1171-1243: The oauthColumns definition is not memoized which
causes a new columns reference each render; wrap the oauthColumns array in
useMemo (like contactChannelColumns and teamColumns) to return a stable
reference for DesignDataTable, e.g. const oauthColumns = useMemo(() => [...],
[/* dependencies */]); and include handleProviderUpdate and any props/state used
inside the column cells in the dependency array (or stabilize
handleProviderUpdate with useCallback) so the memo remains correct and avoids
unnecessary re-renders.
🪄 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: b73d132f-3aca-436a-9f0d-e0535555ee6f

📥 Commits

Reviewing files that changed from the base of the PR and between be6970c and 00d8bc4.

📒 Files selected for processing (1)
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx

@madster456 madster456 requested a review from N2D4 April 20, 2026 19:40
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.

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]/page-client.tsx (1)

1144-1174: ⚠️ Potential issue | 🟠 Major

Use alerts for failed provider toggles instead of toast-only errors.

A missed toast here leaves the user thinking the sign-in / connected-accounts change succeeded when it did not. These failures should go through an alert-based path rather than toast(...).

As per coding guidelines, "For blocking alerts and errors, never use toast, as they are easily missed by the user. Instead, use alerts".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx
around lines 1144 - 1174, The error branch inside handleProviderUpdate currently
uses toast(...) which can be missed; replace the toast-based error notifications
with the app's alert-based error flow (e.g., call the existing
alert/Modal/confirm API used elsewhere like openAlert/showAlert) for both the
KnownErrors.OAuthProviderAccountIdAlreadyUsedForSignIn case and the generic
error case, passing the same title/description/variant content so failures are
surfaced as blocking alerts rather than toasts; keep the success branch using
toast and do not change the function signature of handleProviderUpdate.
♻️ Duplicate comments (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx (1)

1386-1401: ⚠️ Potential issue | 🟠 Major

Heatmap cells are still mouse-only.

TooltipTrigger is wrapping an aria-hidden div, so keyboard and screen-reader users still cannot reach a cell or hear its date/count. Use a focusable element (for example button type="button") and give it an aria-label with the formatted date and activity count.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx
around lines 1386 - 1401, Replace the non-focusable, aria-hidden div inside
TooltipTrigger with a focusable element (e.g., a button type="button") so
keyboard and screen-reader users can reach each heatmap cell; keep the same
classes (including ACTIVITY_COLORS[level]) and remove aria-hidden, and add an
aria-label that combines formatActivityDate(cell.date) and the activity count
(e.g., using cell.activity to render "1 event" vs "N events") so the
TooltipTrigger, TooltipContent, and formatActivityDate usage remain consistent
and accessible.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx:
- Around line 1144-1174: The error branch inside handleProviderUpdate currently
uses toast(...) which can be missed; replace the toast-based error notifications
with the app's alert-based error flow (e.g., call the existing
alert/Modal/confirm API used elsewhere like openAlert/showAlert) for both the
KnownErrors.OAuthProviderAccountIdAlreadyUsedForSignIn case and the generic
error case, passing the same title/description/variant content so failures are
surfaced as blocking alerts rather than toasts; keep the success branch using
toast and do not change the function signature of handleProviderUpdate.

---

Duplicate comments:
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx:
- Around line 1386-1401: Replace the non-focusable, aria-hidden div inside
TooltipTrigger with a focusable element (e.g., a button type="button") so
keyboard and screen-reader users can reach each heatmap cell; keep the same
classes (including ACTIVITY_COLORS[level]) and remove aria-hidden, and add an
aria-label that combines formatActivityDate(cell.date) and the activity count
(e.g., using cell.activity to render "1 event" vs "N events") so the
TooltipTrigger, TooltipContent, and formatActivityDate usage remain consistent
and accessible.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 71c342aa-317e-4210-8fc4-6893a2ab92df

📥 Commits

Reviewing files that changed from the base of the PR and between 93affb5 and 02b5fdf.

📒 Files selected for processing (5)
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx
  • apps/dashboard/src/lib/stack-app-internals.ts
  • packages/stack-shared/src/interface/admin-interface.ts
  • packages/stack-shared/src/interface/admin-metrics.ts
  • packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/stack-shared/src/interface/admin-metrics.ts

@madster456
Copy link
Copy Markdown
Collaborator Author

Disable emails tab in user profile

Comment thread apps/dashboard/src/components/design-components/editable-grid.tsx
@madster456 madster456 merged commit 185bdde into dev May 5, 2026
32 of 34 checks passed
@madster456 madster456 deleted the dashboard/updated_user_page branch May 5, 2026 22:09
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