Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@steven-tey
Copy link
Collaborator

@steven-tey steven-tey commented Oct 28, 2025

Summary by CodeRabbit

  • New Features
    • Added partner programs section in admin with metrics (clicks, leads, conversions, sales, commissions) and status badges; currency/number formatting applied.
  • Refactor
    • Improved layout and spacing; separated workspace and program metric rendering; added visual divider when both sections present.
  • Chores
    • Removed the standalone Workspace ID view/component from the workspace settings UI.

@vercel
Copy link
Contributor

vercel bot commented Oct 28, 2025

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

Project Deployment Preview Updated (UTC)
dub Ready Ready Preview Oct 28, 2025 8:33pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 28, 2025

Walkthrough

This PR removes workspace ID prefixing in several places, adds partner program aggregations and ordering to the impersonate API, enhances the admin UserInfo component to render programs with metrics and status badges, and deletes a client-side WorkspaceId UI file (and its usage).

Changes

Cohort / File(s) Summary
User info — programs & UI
apps/web/app/(ee)/admin.dub.co/(dashboard)/components/user-info.tsx
Added programs to UserInfoProps; introduced programItems and workspaceItems; renders a new Programs grid with status badges (PartnerStatusBadgesStatusBadge), formats currency and numeric metrics, increases container grid gap, and inserts a divider between Workspaces and Programs when both present.
Impersonate API — program aggregations & ordering
apps/web/app/(ee)/api/admin/impersonate/route.ts
Removed prefixWorkspaceId import; added Prisma ordering for workspace projects by totalClicks and partner programs by totalCommissions; included programs aggregation in the returned impersonation payload.
Tinybird record transform — removed prefixing
apps/web/lib/tinybird/record-link.ts
Removed prefixWorkspaceId usage; changed workspace_id transform to pass through (v ? v : "") without prefixing.
Removed client WorkspaceId component
apps/web/ui/workspaces/workspace-id.tsx
Deleted the client-side WorkspaceId component (fetch + copy UI and loading skeleton removed).
Removed WorkspaceId usage from settings page-client
apps/web/app/app.dub.co/(dashboard)/[slug]/settings/(basic-layout)/page-client.tsx
Removed import and JSX <WorkspaceId /> usage; other settings form logic unchanged.

Sequence Diagram(s)

sequenceDiagram
    participant AdminUI as Admin UI
    participant ImpersonateAPI as Impersonate API
    participant DB as Prisma DB
    participant UserInfo as UserInfo Component

    AdminUI->>ImpersonateAPI: request impersonation data
    activate ImpersonateAPI
    ImpersonateAPI->>DB: query workspaces (order by totalClicks)
    DB-->>ImpersonateAPI: workspaces w/ aggregates
    ImpersonateAPI->>DB: query partner programs (order by totalCommissions)
    DB-->>ImpersonateAPI: programs w/ aggregates
    ImpersonateAPI-->>AdminUI: response includes workspaces + programs
    deactivate ImpersonateAPI

    AdminUI->>UserInfo: render with programs array
    activate UserInfo
    UserInfo->>UserInfo: map workspaceItems (nFormatter)
    UserInfo->>UserInfo: map programItems (nFormatter + $ prefix + status badge)
    UserInfo-->>AdminUI: rendered Workspaces and Programs sections (divider if both)
    deactivate UserInfo
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Pay attention to API query changes and returned shape (programs aggregation).
  • Verify removal of prefixing doesn't break analytics/record pipelines (tinybird/record-link).
  • Confirm deleted WorkspaceId component is not required elsewhere and that its behavior is accounted for.

Possibly related PRs

Suggested reviewers

  • devkiran

Poem

🐰 I hopped through code with eager paws,
Programs now shine with badges and claws,
Prefixes fell, the records sing,
Dashboards sparkle in early spring,
A tiny rabbit cheers this springy thing! 🥕✨

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "Remove prefixWorkspaceId from recordLink, update admin" directly reflects the main changes in this changeset. The title accurately captures the two primary themes: the removal of the prefixWorkspaceId utility (from record-link.ts and the impersonate API route) and updates to admin-related components (user-info component enhancements and WorkspaceId component removal). While the title doesn't exhaustively list every modification (such as specific program display additions), it provides clear and specific information that a teammate scanning git history would immediately understand as the primary refactoring work. The title is concise, avoids vague terminology, and directly relates to the main changes in the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch prefix-updates

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f8dd2eb and 9597ddc.

📒 Files selected for processing (2)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/(basic-layout)/page-client.tsx (0 hunks)
  • apps/web/ui/workspaces/workspace-id.tsx (0 hunks)
💤 Files with no reviewable changes (2)
  • apps/web/ui/workspaces/workspace-id.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/(basic-layout)/page-client.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build

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.

Copy link
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: 1

🧹 Nitpick comments (7)
apps/web/app/(ee)/admin.dub.co/(dashboard)/components/user-info.tsx (5)

19-29: Tighten status typing to prevent runtime lookups from breaking.

Constrain status to keys of PartnerStatusBadges so TS catches unsupported values at compile time.

Apply this diff:

   programs: {
     id: string;
     name: string;
     slug: string;
-    status: string;
+    status: keyof typeof PartnerStatusBadges;
     totalClicks: number;
     totalLeads: number;
     totalConversions: number;
     totalSaleAmount: number;
     totalCommissions: number;
   }[];

36-41: Give workspaceItems a keyed union for type-safe access.

This avoids workspace[item.id] being typed as any and ensures only allowed keys are used.

Apply this diff:

-const workspaceItems = [
-  { id: "clicks", label: "Clicks" },
-  { id: "links", label: "Links" },
-  { id: "totalClicks", label: "Total Clicks" },
-  { id: "totalLinks", label: "Total Links" },
-];
+const workspaceItems = [
+  { id: "clicks", label: "Clicks" },
+  { id: "links", label: "Links" },
+  { id: "totalClicks", label: "Total Clicks" },
+  { id: "totalLinks", label: "Total Links" },
+] as const satisfies ReadonlyArray<{
+  id: keyof UserInfoProps["workspaces"][number];
+  label: string;
+}>;

43-49: Same for programItems: type-safe ids and currency flag.

Apply this diff:

-const programItems = [
-  { id: "totalClicks", label: "Total Clicks" },
-  { id: "totalLeads", label: "Total Leads" },
-  { id: "totalConversions", label: "Total Conversions" },
-  { id: "totalSaleAmount", label: "Total Sales", isCurrency: true },
-  { id: "totalCommissions", label: "Total Commissions", isCurrency: true },
-];
+const programItems = [
+  { id: "totalClicks", label: "Total Clicks" },
+  { id: "totalLeads", label: "Total Leads" },
+  { id: "totalConversions", label: "Total Conversions" },
+  { id: "totalSaleAmount", label: "Total Sales", isCurrency: true },
+  { id: "totalCommissions", label: "Total Commissions", isCurrency: true },
+] as const satisfies ReadonlyArray<{
+  id: keyof UserInfoProps["programs"][number];
+  label: string;
+  isCurrency?: boolean;
+}>;

64-103: Use a stable key and keep grid responsive.

  • Prefer workspace.id over slug for React keys.
  • Optional: improve small-screen layout with a 1-col fallback.

Apply this diff:

-          <div className="grid grid-cols-2 gap-4">
+          <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
             {data.workspaces.map((workspace) => (
               <div
-                key={workspace.slug}
+                key={workspace.id}
                 className="flex flex-col space-y-2 rounded-lg border border-neutral-200 p-2"
               >

109-152: Harden StatusBadge lookup and optionally improve currency formatting.

Guard against unknown statuses to avoid runtime errors, and consider a currency formatter for dollar values.

Apply this diff:

                 <div className="flex justify-between text-sm">
                   <span className="font-medium text-neutral-700">Status</span>
-                  <StatusBadge
-                    variant={PartnerStatusBadges[program.status].variant}
-                  >
-                    {PartnerStatusBadges[program.status].label}
-                  </StatusBadge>
+                  <StatusBadge
+                    variant={
+                      PartnerStatusBadges[
+                        program.status as keyof typeof PartnerStatusBadges
+                      ]?.variant ?? "neutral"
+                    }
+                  >
+                    {
+                      PartnerStatusBadges[
+                        program.status as keyof typeof PartnerStatusBadges
+                      ]?.label ?? capitalize(program.status)
+                    }
+                  </StatusBadge>
                 </div>
                 {programItems.map((item) => (
                   <div key={item.id} className="flex justify-between text-sm">
                     <span className="font-medium text-neutral-700">
                       {item.label}
                     </span>
                     <span className="text-neutral-500">
-                      {item.isCurrency
-                        ? `$${nFormatter(program[item.id], { full: true })}`
-                        : nFormatter(program[item.id], { full: true })}
+                      {item.isCurrency
+                        ? `$${nFormatter(program[item.id], { full: true })}`
+                        : nFormatter(program[item.id], { full: true })}
                     </span>
                   </div>
                 ))}

Optional: if available in @dub/utils, prefer a money formatter:

// replace `$${nFormatter(...)}` with formatCurrency(program[item.id])
apps/web/lib/tinybird/record-link.ts (1)

45-49: Remove redundant workspace_id override in recordLinkTB.

The dubLinksMetadataSchema already defines workspace_id with the exact same transformation (.string().nullish().transform((v) => (v ? v : ""))). The extend override duplicates this logic unnecessarily. Removing it maintains identical behavior while keeping a single source of truth.

 export const recordLinkTB = tb.buildIngestEndpoint({
   datasource: "dub_links_metadata",
-  event: dubLinksMetadataSchema.extend({
-    workspace_id: z
-      .string()
-      .nullish()
-      .transform((v) => (v ? v : "")),
-  }),
+  event: dubLinksMetadataSchema,
   wait: true,
 });
apps/web/app/(ee)/api/admin/impersonate/route.ts (1)

41-46: Add index on Project.totalClicks for nested orderBy performance.

Prisma 5.18.0 supports nested orderBy on relations, so the syntax is compatible. However, the totalClicks column lacks an index in the Project model (packages/prisma/schema/workspace.prisma). Ordering by an unindexed column can degrade performance at scale. Add @@index(totalClicks(sort: Desc)) to the Project model.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 69de8fd and f8dd2eb.

📒 Files selected for processing (4)
  • apps/web/app/(ee)/admin.dub.co/(dashboard)/components/user-info.tsx (3 hunks)
  • apps/web/app/(ee)/api/admin/impersonate/route.ts (2 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/(basic-layout)/page-client.tsx (0 hunks)
  • apps/web/lib/tinybird/record-link.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/(basic-layout)/page-client.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/app/(ee)/admin.dub.co/(dashboard)/components/user-info.tsx (1)
apps/web/ui/partners/partner-status-badges.ts (1)
  • PartnerStatusBadges (11-60)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (3)
apps/web/app/(ee)/api/admin/impersonate/route.ts (1)

47-74: Programs selection looks good; ordering by totalCommissions is clear.

apps/web/app/(ee)/admin.dub.co/(dashboard)/components/user-info.tsx (2)

2-3: LGTM on imports.


105-107: Divider conditional looks good.

Comment on lines +91 to +96
programs: response.partners[0].partner.programs.map(
({ program, ...rest }) => ({
...program,
...rest,
}),
),
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix potential crash and include programs from all partners.

response.partners[0] can be undefined, causing a runtime error. Also, limiting to index 0 drops programs from other partners. Flatten safely.

Apply this diff:

-    programs: response.partners[0].partner.programs.map(
-      ({ program, ...rest }) => ({
-        ...program,
-        ...rest,
-      }),
-    ),
+    programs: (response.partners ?? [])
+      .flatMap((p) => p.partner?.programs ?? [])
+      .map(({ program, ...rest }) => ({ ...program, ...rest })),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
programs: response.partners[0].partner.programs.map(
({ program, ...rest }) => ({
...program,
...rest,
}),
),
programs: (response.partners ?? [])
.flatMap((p) => p.partner?.programs ?? [])
.map(({ program, ...rest }) => ({ ...program, ...rest })),
🤖 Prompt for AI Agents
In apps/web/app/(ee)/api/admin/impersonate/route.ts around lines 91 to 96, the
code assumes response.partners[0] exists and only uses its programs which can
crash when partners is undefined and drops programs from other partners; change
this to safely iterate over all partners (guarding for response.partners being
undefined) and flatten their programs, e.g. treat response.partners as an array
(default to []), use flatMap or reduce to collect every partner.programs, and
inside the iterator merge each partner-program item by spreading program and the
rest as before so the result includes programs from all partners without
throwing.

@steven-tey steven-tey merged commit e47732f into main Oct 29, 2025
8 checks passed
@steven-tey steven-tey deleted the prefix-updates branch October 29, 2025 01:18
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