Skip to content

Earnings calculator update#3412

Merged
steven-tey merged 4 commits intomainfrom
earnings-calculator
Feb 4, 2026
Merged

Earnings calculator update#3412
steven-tey merged 4 commits intomainfrom
earnings-calculator

Conversation

@marcusljf
Copy link
Collaborator

@marcusljf marcusljf commented Feb 4, 2026

  • Updates to the style, which removes the brand color as it was diluted and made it look bad.
  • Added a monthly/yearly toggle to the builder, and this only shows in the builder
Content block Modal
CleanShot 2026-02-03 at 16 01 44@2x CleanShot 2026-02-03 at 16 01 53@2x
CleanShot.2026-02-03.at.16.03.02.mp4

Summary by CodeRabbit

  • New Features

    • Monthly/yearly toggle for earnings calculations (defaults to monthly).
    • Interactive earnings slider with live projections and clearer numeric display (shows /mo or /yr).
    • Decorative wave background added to the earnings block.
    • Live preview updates when changing the billing period.
  • Style

    • New slider styling for consistent cross-browser appearance and improved visuals.

- Updates to the style, which removes the brand color as it was diluted and made it look bad.
- Added a monthly/yearly toggle to the builder, and this only shows in the builder
@vercel
Copy link
Contributor

vercel bot commented Feb 4, 2026

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

Project Deployment Actions Updated (UTC)
dub Ready Ready Preview Feb 4, 2026 0:30am

Request Review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 4, 2026

📝 Walkthrough

Walkthrough

Adds an optional billingPeriod enum to the earnings-calculator schema, surfaces a monthly/yearly toggle in the modal, threads billingPeriod into preview and block data, updates earnings calculation/display and slider UI, removes brandColor from group props, and adds a WavePattern SVG component.

Changes

Cohort / File(s) Summary
Schema
apps/web/lib/zod/schemas/program-lander.ts
Added optional billingPeriod: z.enum(["monthly","yearly"]).optional() to the earnings-calculator block schema.
Styles
apps/web/styles/globals.css
Added .earnings-slider styles for cross-browser slider thumb/track and preserved Sonner toast border-radius override.
Modal UI
apps/web/ui/partners/groups/design/lander/modals/earnings-calculator-block-modal.tsx
Added billingPeriod toggle (monthly/yearly) via ToggleGroup + Controller, defaulting to "monthly", propagated billingPeriod into preview/block data; removed preview usage of brandColor.
Block UI & Visuals
apps/web/ui/partners/lander/blocks/earnings-calculator-block.tsx
Dropped brandColor from group prop; added SLIDER_MIN/MAX, isYearly from block.data.billingPeriod, monthly vs yearly earnings math, redesigned UI with dynamic earnings display and gradient slider, added WavePattern usage.
New Component
apps/web/ui/partners/lander/blocks/wave-pattern.tsx
Added WavePattern() React component rendering an SVG wave with masks/gradients for decorative background.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Modal
    participant Preview
    participant Block
    participant Calc

    User->>Modal: Toggle billingPeriod (monthly/yearly)
    Modal->>Modal: Update RHF form value (billingPeriod)
    Modal->>Preview: Re-render with billingPeriod in block data
    Preview->>Block: Pass block data (includes billingPeriod)
    Block->>Calc: Determine isYearly from billingPeriod
    Calc->>Calc: Compute monthlyEarnings (flat or percentage)
    alt isYearly
        Calc->>Calc: displayEarnings = monthlyEarnings * 12
    else
        Calc->>Calc: displayEarnings = monthlyEarnings
    end
    Calc->>Block: Return displayEarnings
    Block->>User: Render earnings, slider, and period suffix (/mo or /yr)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • devkiran

Poem

🐇 I toggled time from month to year,

Sliders hum and numbers cheer,
Waves of SVG glow light and thin,
Earnings hop from out to in,
A rabbit claps — the UI's clear!

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Earnings calculator update' is vague and generic, using a non-descriptive term that doesn't convey the specific nature of the changes (style updates, monthly/yearly toggle addition, brand color removal). Consider a more specific title such as 'Add monthly/yearly toggle to earnings calculator and remove brand color' to better reflect the key changes.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 earnings-calculator

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

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/lander/blocks/earnings-calculator-block.tsx (1)

35-68: ⚠️ Potential issue | 🟡 Minor

Yearly calculation is floored too early.
Flooring monthly before multiplying by 12 can undercount yearly totals when monthly has cents. Consider rounding after the period calculation.

🔧 Proposed fix (round after period selection)
-  const monthlyEarnings = Math.floor(
-    group.saleReward.type === "flat"
-      ? (value * rewardAmount) / 100
-      : revenue * (rewardAmount / 100),
-  );
-
-  const displayEarnings = isYearly ? monthlyEarnings * 12 : monthlyEarnings;
+  const monthlyEarnings =
+    group.saleReward.type === "flat"
+      ? (value * rewardAmount) / 100
+      : revenue * (rewardAmount / 100);
+
+  const displayEarnings = isYearly ? monthlyEarnings * 12 : monthlyEarnings;
+  const roundedDisplayEarnings = Math.floor(displayEarnings);
-            <NumberFlow
-              value={displayEarnings}
+            <NumberFlow
+              value={roundedDisplayEarnings}
🤖 Fix all issues with AI agents
In `@apps/web/ui/partners/lander/blocks/earnings-calculator-block.tsx`:
- Around line 76-87: The range input lacks an accessible label; add an
associated label element (or an id on the existing descriptive element) and
reference it from the input via aria-labelledby so screen readers know what the
slider controls: give the descriptive paragraph (currently rendering <NumberFlow
value={value} /> customer sales) a stable id like `${id}-label` and set the
input (id `${id}-slider`) to include aria-labelledby={`${id}-label`}; keep the
existing onChange/setValue handling and SLIDER_MIN/SLIDER_MAX usage intact.

@steven-tey
Copy link
Collaborator

@coderabbitai full review please, any breaking changes

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 4, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read all changed files to understand the implementation
echo "=== program-lander.ts schema ===" 
cat apps/web/lib/zod/schemas/program-lander.ts

echo -e "\n=== earnings-calculator-block.tsx ===" 
cat apps/web/ui/partners/lander/blocks/earnings-calculator-block.tsx

echo -e "\n=== earnings-calculator-block-modal.tsx ===" 
cat apps/web/ui/partners/groups/design/lander/modals/earnings-calculator-block-modal.tsx

echo -e "\n=== wave-pattern.tsx ===" 
cat apps/web/ui/partners/lander/blocks/wave-pattern.tsx

echo -e "\n=== globals.css (earnings-slider section) ===" 
# Get the earnings-slider section from globals.css
tail -50 apps/web/styles/globals.css

Length of output: 47216


🏁 Script executed:

#!/bin/bash
# Search for all usages of EarningsCalculatorBlock to assess breaking change impact
echo "=== Searching for EarningsCalculatorBlock usages ==="
rg -nC3 'EarningsCalculatorBlock' --type=tsx --type=ts -g '!*.test.*' -g '!*.spec.*'

Length of output: 80


🏁 Script executed:

#!/bin/bash
# Search for brandColor usage in the context of earnings calculator
echo "=== Searching for brandColor in earnings/lander context ==="
rg -nC3 'brandColor' apps/web/ui/partners --type=tsx --type=ts

Length of output: 87


🏁 Script executed:

#!/bin/bash
# Check the GroupProps type definition to understand the interface
echo "=== Finding GroupProps definition ==="
rg -nC5 'type GroupProps|interface GroupProps' --type=tsx --type=ts

Length of output: 65


🏁 Script executed:

#!/bin/bash
# Search for EarningsCalculatorBlock usages (without type filters)
echo "=== Searching for EarningsCalculatorBlock component usages ==="
rg -n 'EarningsCalculatorBlock' -g '*.ts' -g '*.tsx' -g '!*.test.*' -g '!*.spec.*'

Length of output: 3359


🏁 Script executed:

#!/bin/bash
# Search for brandColor usage in partners directory
echo "=== Searching for brandColor references ==="
rg -n 'brandColor' apps/web/ui/partners -g '*.ts' -g '*.tsx'

Length of output: 3782


🏁 Script executed:

#!/bin/bash
# Find GroupProps type definition
echo "=== Finding GroupProps type definition ==="
rg -n 'type GroupProps|interface GroupProps' -g '*.ts' -g '*.tsx' -A10

Length of output: 699


🏁 Script executed:

#!/bin/bash
# Check for any imports of earnings-calculator-block to see consumers
echo "=== Finding files that import EarningsCalculatorBlock ==="
rg -n "from.*earnings-calculator-block" -g '*.ts' -g '*.tsx'

Length of output: 512


🏁 Script executed:

#!/bin/bash
# Read the blocks index file to see how EarningsCalculatorBlock is used
echo "=== Checking blocks/index.tsx to see component usage pattern ==="
cat apps/web/ui/partners/lander/blocks/index.tsx

Length of output: 774


🏁 Script executed:

#!/bin/bash
# Check the GroupSchema to understand the full structure
echo "=== Finding GroupSchema definition ==="
rg -n 'const GroupSchema|export const GroupSchema' -g '*.ts' -g '*.tsx' -A30

Length of output: 4029


🏁 Script executed:

#!/bin/bash
# Check if there's a git diff available to see exactly what changed
echo "=== Checking git diff for earnings-calculator-block.tsx ==="
git diff HEAD~1 apps/web/ui/partners/lander/blocks/earnings-calculator-block.tsx 2>/dev/null || echo "Git diff not available"

Length of output: 59


🏁 Script executed:

#!/bin/bash
# Search for usages of "saleReward" and "brandColor" together to find call sites
echo "=== Finding potential call sites with both saleReward and brandColor ==="
rg -n 'saleReward.*brandColor|brandColor.*saleReward' -g '*.ts' -g '*.tsx' -C3

Length of output: 72


🏁 Script executed:

#!/bin/bash
# Find where BLOCK_COMPONENTS is used to understand the rendering pattern
echo "=== Finding BLOCK_COMPONENTS usage ==="
rg -n 'BLOCK_COMPONENTS' -g '*.ts' -g '*.tsx' -C5

Length of output: 14781


🏁 Script executed:

#!/bin/bash
# Check the lander preview to see how blocks are rendered
echo "=== Checking lander-preview.tsx for block rendering ==="
cat apps/web/ui/partners/groups/design/previews/lander-preview.tsx | head -200

Length of output: 6732


🏁 Script executed:

#!/bin/bash
# Search for where blocks are mapped/rendered in the lander
echo "=== Finding block rendering in lander ==="
rg -n 'blocks?.map|block\.type' apps/web/ui/partners -g '*.tsx' -C3

Length of output: 893


🏁 Script executed:

#!/bin/bash
# Check the previous git commit to see the actual diff
echo "=== Checking git log for recent commits ==="
git log --oneline -5

Length of output: 302


🏁 Script executed:

#!/bin/bash
# Check the marketplace page to see the full context of how it renders blocks
echo "=== Checking marketplace page block rendering context ==="
cat apps/web/app/\(ee\)/partners.dub.co/\(dashboard\)/programs/marketplace/\[programSlug\]/page.tsx | grep -A20 -B5 "BLOCK_COMPONENTS\[block.type\]"

Length of output: 638


🏁 Script executed:

#!/bin/bash
# Check if there are TypeScript errors by looking at the type of group passed
echo "=== Examining the group object structure in call sites ==="
rg -n "group={" apps/web/app -g '*.tsx' -A1 -B1 | grep -A2 -B2 "Component.*block"

Length of output: 136


🏁 Script executed:

#!/bin/bash
# Look at git diff for the earnings calculator to see what exactly changed
echo "=== Git diff for earnings-calculator-block.tsx ==="
git diff d9e9fba5a e1a0dd93e -- apps/web/ui/partners/lander/blocks/earnings-calculator-block.tsx

Length of output: 1579


🏁 Script executed:

#!/bin/bash
# Check if there were changes to how brandColor was previously used in the component
echo "=== Checking previous version of earnings-calculator-block.tsx ==="
git show d9e9fba5a:apps/web/ui/partners/lander/blocks/earnings-calculator-block.tsx | head -80

Length of output: 3014


@steven-tey, I'll provide a comprehensive review with focus on breaking changes.

⚠️ Breaking Changes

1. EarningsCalculatorBlock Component Signature Changed

File: apps/web/ui/partners/lander/blocks/earnings-calculator-block.tsx

The component's public props interface has changed:

// Before
group: Pick<GroupProps, "saleReward" | "brandColor">

// After  
group: Pick<GroupProps, "saleReward">

Impact: This is a breaking change for the component's public API. The brandColor property has been removed from the required props.

Mitigation: All internal call sites pass full group objects (which include brandColor), so they won't break at runtime. TypeScript allows extra properties in object types, so existing consumers will continue to work. However, any external TypeScript code that was explicitly constructing the props object with brandColor may need updates.

2. Schema Field Rename

File: apps/web/lib/zod/schemas/program-lander.ts

The field name changed from earningsTime to billingPeriod:

billingPeriod: z.enum(["monthly", "yearly"]).optional()

Impact: This is backward compatible because:

  • The field is marked as optional()
  • The modal defaults to "monthly" when the field is undefined
  • Existing data without this field will continue to work

🐛 Pre-existing Bug Found

File: apps/web/app/(ee)/partners.dub.co/(dashboard)/programs/marketplace/[programSlug]/page.tsx

The marketplace page passes only group={{ logo: program.logo }} to the EarningsCalculatorBlock, but the component requires saleReward to function. This will cause the component to return null due to the early return check if (!group?.saleReward) return null;.

Recommendation: Update the marketplace page to pass the required saleReward:

group={{ logo: program.logo, saleReward: program.saleReward }}

✅ Other Changes Review

New WavePattern Component

  • Clean SVG component with proper accessibility (uses useId for unique IDs)
  • Well-structured with gradient and clip-path definitions
  • No concerns

Accessibility Improvements

Added proper ARIA labeling to the slider with aria-labelledby linking to the label element. This is a good improvement for screen reader users.

UI/Styling Updates

  • New custom CSS for .earnings-slider with cross-browser support (WebKit and Mozilla)
  • Redesigned calculator layout with improved visual hierarchy
  • Monthly/yearly toggle added to the modal (builder-only, not visible in public view)
  • Clean, professional styling with proper spacing and borders

Modal Changes

  • New ToggleGroup for "Earnings time" selection (Monthly/Yearly)
  • Form properly wired with react-hook-form's Controller
  • Preview updates reactively based on billingPeriod changes
  • Removed brandColor from preview (no longer needed)

Summary

Breaking Changes:

  1. Minor - brandColor removed from EarningsCalculatorBlock props (mitigated by internal usage patterns)
  2. None - billingPeriod field addition is backward compatible

Issues Found:

  1. ⚠️ Pre-existing bug in marketplace page (doesn't pass saleReward)

Recommendation: This PR is safe to merge. The breaking change to the component signature is well-contained and won't affect existing functionality. Consider fixing the marketplace page bug in a follow-up PR.

@steven-tey steven-tey merged commit 855aa5f into main Feb 4, 2026
9 of 11 checks passed
@steven-tey steven-tey deleted the earnings-calculator branch February 4, 2026 00:31
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