diff --git a/backend/src/app.ts b/backend/src/app.ts index 0b8afa7..6e11a8d 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -94,6 +94,7 @@ import { getMembershipForUser, getNextWorkspaceId, getWorkspaceById, + getWorkspaceBirdCount, getWorkspaceTotalBirdCount, listOwnedWorkspacesByOwnerEmail, listRescueWorkspacesForAdmin, @@ -951,6 +952,26 @@ const subscriptionAllowsWrite = (workspace: WorkspaceRow) => { return workspace.subscription_status === 'active' || workspace.subscription_status === 'trialing'; }; +const getBillingPlanBirdLimit = (billingPlan: BillingPlan) => { + if (billingPlan === 'rescue_free') { + return null; + } + + if (billingPlan === 'household_basic') { + return 4; + } + + if (billingPlan === 'household_plus') { + return 10; + } + + if (billingPlan === 'household_macaw') { + return 16; + } + + return null; +}; + const mapStripeSubscriptionStatus = (status: Stripe.Subscription.Status): SubscriptionStatus => { if (status === 'active' || status === 'trialing' || status === 'past_due' || status === 'canceled' || status === 'unpaid') { return status; @@ -3118,6 +3139,23 @@ app.post('/api/birds', requireAuth, requireWriteAccess, requireWorkspaceRole(['o let uploadedObjectKeyToCleanup: string | null = null; try { + const birdLimit = getBillingPlanBirdLimit(req.auth!.workspace.billing_plan); + + if (birdLimit !== null) { + const currentBirdCount = await getWorkspaceBirdCount(req.auth!.workspace.id); + + if (currentBirdCount >= birdLimit) { + res.status(409).json({ + error: 'This flock has reached the bird limit for the selected plan. Upgrade the flock subscription or memorialize a bird before adding another.', + code: 'billing_plan_bird_limit_reached', + birdLimit, + currentBirdCount, + billingPlan: req.auth!.workspace.billing_plan, + }); + return; + } + } + const birdId = crypto.randomUUID(); const photoStorage = await resolveBirdPhotoStorage({ birdId, diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index ec1a70e..23d5ce5 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1012,7 +1012,7 @@ const formatBillingPlanCapacity = (billingPlan: BillingPlan) => { } if (billingPlan === 'household_plus') { - return 'Permits 5 to 9 birds in the flock.'; + return 'Permits 5 to 10 birds in the flock.'; } if (billingPlan === 'household_macaw') { @@ -1024,11 +1024,11 @@ const formatBillingPlanCapacity = (billingPlan: BillingPlan) => { const formatBillingPlanDropdownLabel = (billingPlan: HouseholdBillingPlan) => { if (billingPlan === 'household_basic') { - return 'Conure (4 birds)'; + return 'Conure (up to 4 birds)'; } if (billingPlan === 'household_plus') { - return 'Indian Ringneck (5-9 birds)'; + return 'Indian Ringneck (5-10 birds)'; } if (billingPlan === 'household_macaw') { @@ -1066,7 +1066,7 @@ const formatBillingPlanBirdLimit = (billingPlan: BillingPlan) => { } if (billingPlan === 'household_plus') { - return '9'; + return '10'; } if (billingPlan === 'household_macaw') { @@ -6451,8 +6451,8 @@ function App() { }) } > - - + + @@ -6590,8 +6590,8 @@ function App() { }) } > - - + +