+
Skip to content

Conversation

TWilson023
Copy link
Collaborator

@TWilson023 TWilson023 commented Oct 6, 2025

Screenshot 2025-10-06 at 11 33 27 AM Screenshot 2025-10-06 at 11 33 11 AM

Summary by CodeRabbit

  • New Features

    • Real-time partner-invite usage endpoint and header widget showing usage, limit, and remaining (7-day rolling).
    • Client UI component displays remaining invites with tooltip and contextual styling.
  • Improvements

    • Invite actions now respect usage limits: invite buttons and shortcuts disable when limits reached.
    • Empty state shows "Clear all filters" only when relevant; animated empty state scales with config.
  • Subscription & Billing

    • Workspace limits persisted and updated on plan changes; Enterprise gets a non‑zero invites quota.
  • Documentation

    • Clarified clickId description for deferred lead tracking.

Copy link
Contributor

vercel bot commented Oct 6, 2025

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

Project Deployment Preview Updated (UTC)
dub Ready Ready Preview Oct 6, 2025 11:38pm

Copy link
Contributor

coderabbitai bot commented Oct 6, 2025

Walkthrough

Adds network-invite limits and weekly usage tracking: plan limit fields, DB/schema field and persistence, an enterprise-only GET API and server util, SWR hook and UI components, enforcement in the invite action, plus minor UI and schema tweaks.

Changes

Cohort / File(s) Summary
Limits & Schema
packages/utils/src/constants/pricing.tsx, packages/prisma/schema/workspace.prisma, apps/web/lib/zod/schemas/workspaces.ts
Add networkInvites limit to plans, add networkInvitesLimit Int field to Project, and expose networkInvitesLimit on WorkspaceSchema.
Plan update (Stripe webhooks)
apps/web/app/(ee)/api/stripe/webhook/checkout-session-completed.ts, apps/web/app/(ee)/api/stripe/webhook/customer-subscription-deleted.ts, apps/web/app/(ee)/api/stripe/webhook/utils/update-workspace-plan.ts
Persist networkInvitesLimit on checkout and when reverting to free plan.
Server API & Util
apps/web/app/(ee)/api/network/partners/invites-usage/route.ts, apps/web/lib/api/partners/get-network-invites-usage.ts
New enterprise-only GET route returning { usage, limit, remaining }; server util counts invites since billing cycle start.
Client data hook
apps/web/lib/swr/use-partner-network-invites-usage.ts
New SWR hook fetching invites usage endpoint and exposing usage, limit, remaining, isLoading, error.
Invite enforcement (action)
apps/web/lib/actions/partners/invite-partner-from-network.ts
Check current usage against workspace.networkInvitesLimit and throw when limit reached.
UI: component & sheet integration
apps/web/ui/partners/partner-network/invites-usage.tsx, apps/web/ui/partners/partner-network/network-partner-sheet.tsx
New InvitesUsage component; show remaining invites in partner sheet, disable invite button and shortcut when out of invites.
Page integration & imports
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/network/page.tsx, apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/network/page-client.tsx, apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/network/network-empty-state.tsx
Inject InvitesUsage into page header controls, adjust import path for partner sheet, and conditionally render clear-filters button.
UI tweaks
apps/web/ui/shared/animated-empty-state.tsx
Render dynamic number of Cards (cardCount * 2 instead of fixed 6).
Misc schema copy
apps/web/lib/zod/schemas/leads.ts
Update clickId description text.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant P as Network Page
  participant H as usePartnerNetworkInvitesUsage
  participant API as GET /api/network/partners/invites-usage
  participant S as getNetworkInvitesUsage
  participant DB as Database

  U->>P: Open partner network page
  P->>H: Initialize (workspaceId)
  H->>API: GET usage
  API->>S: compute usage(workspaceId)
  S->>DB: count invites since billing start
  DB-->>S: usage count
  S-->>API: usage
  API-->>H: {usage, limit, remaining}
  H-->>P: render InvitesUsage with remaining
Loading
sequenceDiagram
  autonumber
  actor U as User
  participant Sheet as Partner Sheet
  participant H as usePartnerNetworkInvitesUsage
  participant Action as invitePartnerFromNetworkAction
  participant S as getNetworkInvitesUsage
  participant DB as Database

  U->>Sheet: Open sheet
  Sheet->>H: fetch remaining
  H->>API: GET /invites-usage
  API->>S: compute usage
  S->>DB: count invites
  DB-->>S: usage
  S-->>Sheet: remaining
  alt remaining == 0
    Sheet->>U: disable invite UI/shortcut
  else
    U->>Sheet: Click Invite
    Sheet->>Action: invitePartnerFromNetworkAction
    Action->>S: getNetworkInvitesUsage
    S->>DB: count invites
    DB-->>S: usage
    S-->>Action: usage
    alt usage >= limit
      Action-->>Sheet: Error "limit reached"
    else
      Action-->>Sheet: proceed with invite
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Partner Network #2886 — Related partner-network invite flow changes; overlaps with invite limit and usage checks.

Suggested reviewers

  • devkiran

Poem

A rabbit tallies invites in a row,
Hops and counts how many may go.
Tooltip sparkles, numbers in sight,
When they run out, the button stays tight.
Hop on—limits kept, but joy’s still bright. 🐇✉️

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 title “Partner network invite limits” succinctly and accurately reflects the main focus of the changeset, which implements invitation limits for the partner network across the API, schema, actions, and UI.
✨ 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 partner-network-invite-limit

📜 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 bffe343 and 65b7687.

📒 Files selected for processing (1)
  • apps/web/ui/partners/partner-network/invites-usage.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/ui/partners/partner-network/invites-usage.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: 3

Caution

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

⚠️ Outside diff range comments (1)
apps/web/ui/partners/partner-network/network-partner-sheet.tsx (1)

197-197: Disable keyboard shortcut when button is disabled.

The global keyboard shortcut "s" (line 197) remains active even when the invite button is disabled (disabled = true at line 205). This creates inconsistent UX where pressing "s" opens the confirmation modal even when the button shows no shortcut indicator and is visually disabled.

Apply this diff to conditionally register the shortcut:

-  useKeyboardShortcut("s", () => setShowConfirmModal(true), { sheet: true });
+  useKeyboardShortcut("s", () => setShowConfirmModal(true), { 
+    sheet: true,
+    enabled: !disabled
+  });

Alternatively, if the useKeyboardShortcut hook doesn't support an enabled option, conditionally call it:

-  useKeyboardShortcut("s", () => setShowConfirmModal(true), { sheet: true });
+  if (!disabled) {
+    useKeyboardShortcut("s", () => setShowConfirmModal(true), { sheet: true });
+  }

Note: The second approach may cause issues with React hooks rules. Verify the useKeyboardShortcut API to determine the best solution.

♻️ Duplicate comments (2)
apps/web/app/(ee)/api/stripe/webhook/checkout-session-completed.ts (1)

72-72: Verify that networkInvites is defined in all plan limits.

The non-null assertion on plan.limits.networkInvites! will cause a runtime error if any plan doesn't define this limit. See similar comment on update-workspace-plan.ts.

apps/web/app/(ee)/api/stripe/webhook/customer-subscription-deleted.ts (1)

121-121: Verify that networkInvites is defined in FREE_PLAN limits.

The non-null assertion on FREE_PLAN.limits.networkInvites! will cause a runtime error if the free plan doesn't define this limit. See similar comments on other webhook handlers.

🧹 Nitpick comments (2)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/network/network-empty-state.tsx (1)

48-57: LGTM! Sensible UI improvement.

The conditional rendering of the "Clear all filters" button aligns perfectly with the filter state and the description text above (lines 19-30). When no filters are active, hiding the button improves the UX by avoiding a misleading or non-functional action.

Optional refinement:

The condition isFiltered || isStarred is repeated at lines 19 and 49. Consider extracting it to a constant for better maintainability:

+const hasActiveFilters = isFiltered || isStarred;
+
 return (
   <AnimatedEmptyState
     title="No partners found"
     description={
-      isFiltered || isStarred ? (
+      hasActiveFilters ? (
         <>
           Press{" "}
           <span className="text-content-default bg-bg-emphasis rounded-md px-1 py-0.5 text-xs font-semibold">
             Esc
           </span>{" "}
           to clear all filters.
         </>
       ) : (
         "There are no partners for you to discover yet."
       )
     }
     className="border-none md:min-h-[400px]"
     cardClassName="py-3"
     cardCount={2}
     cardContent={(idx) => (
       <div className="flex grow items-center gap-4">
         {idx % 2 === 0 || isStarred ? (
           <StarFill className="size-3 shrink-0 text-amber-500" />
         ) : (
           <Star className="text-content-muted size-3 shrink-0" />
         )}
         <DemoAvatar className="text-content-default size-9 shrink-0" />
         <div className="flex grow flex-col gap-2">
           <div className="h-2.5 w-full min-w-0 rounded bg-neutral-200" />
           <div className="h-2.5 w-12 min-w-0 rounded bg-neutral-200" />
         </div>
       </div>
     )}
     addButton={
-      isFiltered || isStarred ? (
+      hasActiveFilters ? (
         <Button
           type="button"
           text="Clear all filters"
           className="h-9 rounded-lg"
           onClick={onClearAllFilters}
         />
       ) : undefined
     }
   />
 );
apps/web/lib/actions/partners/invite-partner-from-network.ts (1)

25-28: Consider enhancing the error message with context.

While the current error message is clear, providing additional context could improve the user experience:

-    if (networkInvitesUsage >= workspace.networkInvitesLimit)
-      throw new Error(
-        "You have reached your partner network invitations limit.",
-      );
+    if (networkInvitesUsage >= workspace.networkInvitesLimit) {
+      throw new Error(
+        `You have reached your partner network invitations limit (${networkInvitesUsage}/${workspace.networkInvitesLimit} used). This limit resets on a rolling 7-day basis.`,
+      );
+    }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b69404d and a733ebc.

📒 Files selected for processing (17)
  • apps/web/app/(ee)/api/network/partners/invites-usage/route.ts (1 hunks)
  • apps/web/app/(ee)/api/stripe/webhook/checkout-session-completed.ts (1 hunks)
  • apps/web/app/(ee)/api/stripe/webhook/customer-subscription-deleted.ts (1 hunks)
  • apps/web/app/(ee)/api/stripe/webhook/utils/update-workspace-plan.ts (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/network/network-empty-state.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/network/page-client.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/network/page.tsx (1 hunks)
  • apps/web/lib/actions/partners/invite-partner-from-network.ts (2 hunks)
  • apps/web/lib/api/partners/get-network-invites-usage.ts (1 hunks)
  • apps/web/lib/swr/use-partner-network-invites-usage.ts (1 hunks)
  • apps/web/lib/zod/schemas/workspaces.ts (1 hunks)
  • apps/web/ui/partners/partner-network/invites-usage.tsx (1 hunks)
  • apps/web/ui/partners/partner-network/network-partner-sheet.tsx (4 hunks)
  • apps/web/ui/shared/animated-empty-state.tsx (1 hunks)
  • packages/prisma/schema/network.prisma (1 hunks)
  • packages/prisma/schema/workspace.prisma (1 hunks)
  • packages/utils/src/constants/pricing.tsx (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (6)
apps/web/app/(ee)/api/network/partners/invites-usage/route.ts (2)
apps/web/lib/auth/workspace.ts (1)
  • withWorkspace (42-436)
apps/web/lib/api/partners/get-network-invites-usage.ts (1)
  • getNetworkInvitesUsage (3-21)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/network/page.tsx (2)
apps/web/ui/layout/page-content/index.tsx (1)
  • PageContent (11-100)
apps/web/ui/partners/partner-network/invites-usage.tsx (1)
  • InvitesUsage (7-47)
apps/web/ui/partners/partner-network/invites-usage.tsx (1)
apps/web/lib/swr/use-partner-network-invites-usage.ts (1)
  • usePartnerNetworkInvitesUsage (5-34)
apps/web/ui/partners/partner-network/network-partner-sheet.tsx (2)
apps/web/lib/swr/use-partner-network-invites-usage.ts (1)
  • usePartnerNetworkInvitesUsage (5-34)
apps/web/ui/partners/partner-network/invites-usage.tsx (1)
  • InvitesUsage (7-47)
apps/web/lib/actions/partners/invite-partner-from-network.ts (1)
apps/web/lib/api/partners/get-network-invites-usage.ts (1)
  • getNetworkInvitesUsage (3-21)
apps/web/app/(ee)/api/stripe/webhook/customer-subscription-deleted.ts (1)
packages/utils/src/constants/pricing.tsx (1)
  • FREE_PLAN (361-361)
⏰ 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 (13)
apps/web/ui/shared/animated-empty-state.tsx (1)

47-47: LGTM! Dynamic card count enhances flexibility.

The change from a hardcoded value to cardCount * 2 makes the component more configurable while maintaining backward compatibility (default 3 × 2 = 6 cards). The multiplication by 2 correctly supports the infinite scroll animation by rendering duplicate sets, and the idx % cardCount on line 50 properly cycles through content indices.

Edge cases (zero or negative cardCount) are handled gracefully by JavaScript's array constructor behavior, which creates an empty array in such cases.

packages/prisma/schema/network.prisma (1)

18-18: LGTM!

The composite index on [programId, invitedAt] appropriately supports queries for counting network invites within time ranges per program, which aligns with the 7-day rolling limit feature introduced in this PR.

apps/web/lib/zod/schemas/workspaces.ts (1)

71-73: LGTM!

The networkInvitesLimit field is properly defined and follows the same pattern as other workspace limit fields.

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/network/page-client.tsx (1)

15-15: LGTM!

Import path update reflects the reorganization of partner-related UI components.

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/network/page.tsx (1)

3-3: LGTM!

Clean integration of the InvitesUsage component to display remaining network invites in the page header.

apps/web/app/(ee)/api/stripe/webhook/utils/update-workspace-plan.ts (1)

63-63: No action needed for networkInvites non-null assertion
All plans in packages/utils/src/constants/pricing.tsx include a networkInvites limit, so the non-null assertion is safe.

apps/web/ui/partners/partner-network/network-partner-sheet.tsx (2)

201-205: LGTM! Clean integration of invites usage.

The hook integration correctly computes the disabled state and only fetches usage data when needed (enabled: !alreadyInvited).


210-215: LGTM! Proper conditional rendering.

The InvitesUsage component is correctly rendered only when the partner hasn't been invited yet, which aligns with the UX requirements.

packages/utils/src/constants/pricing.tsx (1)

63-63: LGTM! Consistent plan limit definitions.

The networkInvites limits are correctly set across all plans, with only Enterprise receiving the 20 invites allowance while all other plans default to 0. This aligns with the enterprise-only API endpoint restriction.

Also applies to: 86-86, 161-161, 260-260, 352-352

apps/web/app/(ee)/api/network/partners/invites-usage/route.ts (1)

6-21: LGTM! Proper plan enforcement and safe calculation.

The route correctly:

  • Restricts access to enterprise plan workspaces only
  • Uses Math.max(0, ...) to ensure remaining never goes negative
  • Returns structured data that the client hook expects

The 403 response for non-enterprise workspaces is handled gracefully by the client (the UI component returns null when remaining === undefined).

apps/web/ui/partners/partner-network/invites-usage.tsx (2)

13-25: Update tooltip text to match backend implementation.

The tooltip claims "Invite limits are based on a 7-day rolling period," but the backend implementation in apps/web/lib/api/partners/get-network-invites-usage.ts counts ALL invited partners without any date filtering. This creates a discrepancy between the UI messaging and actual behavior.

Either:

  1. Update the backend to implement the 7-day rolling period (see comment in get-network-invites-usage.ts), or
  2. Update this tooltip text to accurately describe the actual limit behavior

If choosing option 2, consider this revised text:

-          Invite limits are based on a 7-day rolling period. If you need more
-          weekly invites,{" "}
+          Invite limits are refreshed based on your plan. If you need a higher
+          limit,{" "}

29-44: LGTM! Excellent UX with progressive visual indicators.

The component provides clear visual feedback:

  • Shows remaining count prominently
  • Applies violet emphasis when count is low (1-5)
  • Dims text when depleted (0)
  • Responsive text with mobile-friendly abbreviation
apps/web/lib/swr/use-partner-network-invites-usage.ts (1)

5-34: LGTM! Well-structured SWR hook with proper caching.

The hook correctly:

  • Conditionally fetches based on workspaceId and enabled flag
  • Uses keepPreviousData to prevent UI flicker during revalidation
  • Accepts custom SWR configuration via swrOpts
  • Returns a clean API with destructured data plus loading/error states

Note: The workspaceId query parameter in the URL (line 21) serves as an SWR cache key rather than being consumed by the API endpoint, which retrieves the workspace from the authentication context. This is an intentional and common pattern.

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

🧹 Nitpick comments (1)
apps/web/lib/api/partners/get-network-invites-usage.ts (1)

8-18: 7-day rolling period now correctly implemented.

The critical issue from the previous review has been resolved—this implementation now properly filters invitedAt to within the last 7 days, matching the UI's advertised "7-day rolling period."

Optional: Extract date calculation for improved readability.

Consider extracting the date calculation to a named constant for clarity:

 export async function getNetworkInvitesUsage({
   workspaceId,
 }: {
   workspaceId: string;
 }) {
+  const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
+
   const invites = await prisma.discoveredPartner.aggregate({
     _count: true,
     where: {
       program: {
         workspaceId,
       },
       invitedAt: {
-        gt: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
+        gt: sevenDaysAgo,
       },
     },
   });

   return invites._count;
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a733ebc and 8f32390.

📒 Files selected for processing (4)
  • apps/web/lib/api/partners/get-network-invites-usage.ts (1 hunks)
  • apps/web/lib/zod/schemas/leads.ts (1 hunks)
  • apps/web/ui/partners/partner-network/invites-usage.tsx (1 hunks)
  • apps/web/ui/partners/partner-network/network-partner-sheet.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/ui/partners/partner-network/network-partner-sheet.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-06T15:48:14.178Z
Learnt from: TWilson023
PR: dubinc/dub#2935
File: apps/web/lib/actions/partners/invite-partner-from-network.ts:21-28
Timestamp: 2025-10-06T15:48:14.178Z
Learning: For the network invites limit check in apps/web/lib/actions/partners/invite-partner-from-network.ts, the team accepts that concurrent invites may bypass the limit due to race conditions. Perfect atomicity is not required for this feature.

Applied to files:

  • apps/web/lib/api/partners/get-network-invites-usage.ts
🧬 Code graph analysis (1)
apps/web/ui/partners/partner-network/invites-usage.tsx (1)
apps/web/lib/swr/use-partner-network-invites-usage.ts (1)
  • usePartnerNetworkInvitesUsage (5-34)
⏰ 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 (2)
apps/web/lib/zod/schemas/leads.ts (1)

12-12: Documentation improvement enhances API clarity.

The additional bracketed note clearly explains the deferred lead tracking behavior when an empty clickId is provided, improving developer experience.

apps/web/ui/partners/partner-network/invites-usage.tsx (1)

7-46: LGTM! Well-designed UI component with good UX.

The component properly:

  • Reflects the backend's 7-day rolling period (line 14 matches the implementation in get-network-invites-usage.ts)
  • Provides visual feedback with color coding (violet for 1-5 remaining, subtle for 0)
  • Handles loading states gracefully by returning null when remaining is undefined
  • Offers a clear upgrade path via the sales contact link
  • Maintains clarity on mobile with responsive text ("5 remaining" vs "5 invites remaining")

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8f32390 and bffe343.

📒 Files selected for processing (3)
  • apps/web/app/(ee)/api/network/partners/invites-usage/route.ts (1 hunks)
  • apps/web/lib/actions/partners/invite-partner-from-network.ts (2 hunks)
  • apps/web/lib/api/partners/get-network-invites-usage.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/app/(ee)/api/network/partners/invites-usage/route.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: TWilson023
PR: dubinc/dub#2935
File: apps/web/lib/actions/partners/invite-partner-from-network.ts:21-28
Timestamp: 2025-10-06T15:48:14.178Z
Learning: For the network invites limit check in apps/web/lib/actions/partners/invite-partner-from-network.ts, the team accepts that concurrent invites may bypass the limit due to race conditions. Perfect atomicity is not required for this feature.
📚 Learning: 2025-10-06T15:48:14.178Z
Learnt from: TWilson023
PR: dubinc/dub#2935
File: apps/web/lib/actions/partners/invite-partner-from-network.ts:21-28
Timestamp: 2025-10-06T15:48:14.178Z
Learning: For the network invites limit check in apps/web/lib/actions/partners/invite-partner-from-network.ts, the team accepts that concurrent invites may bypass the limit due to race conditions. Perfect atomicity is not required for this feature.

Applied to files:

  • apps/web/lib/actions/partners/invite-partner-from-network.ts
🧬 Code graph analysis (1)
apps/web/lib/actions/partners/invite-partner-from-network.ts (1)
apps/web/lib/api/partners/get-network-invites-usage.ts (1)
  • getNetworkInvitesUsage (5-21)
⏰ 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/lib/actions/partners/invite-partner-from-network.ts (2)

6-6: LGTM!

The import of getNetworkInvitesUsage is correctly placed and will be used to enforce invite limits.


21-26: LGTM!

The usage check correctly enforces the network invites limit before creating an invite. The error message is clear and user-friendly.

Note: As per learnings, the team accepts that concurrent invites may bypass the limit due to race conditions between the check and invite creation.

apps/web/lib/api/partners/get-network-invites-usage.ts (1)

14-16: Consider using gte to include invites at the exact billing cycle start
getBillingStartDate returns a Date at 00:00:00 local time—using gt will exclude invites timestamped exactly at the start of the billing day. If you intend to count all invites from the cycle start onward, switch gt to gte in the invitedAt filter.

@steven-tey steven-tey merged commit 01f4320 into main Oct 6, 2025
7 of 8 checks passed
@steven-tey steven-tey deleted the partner-network-invite-limit branch October 6, 2025 23:43
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

点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载