Skip to content

feat(config): add-ons pricing source of truth#19

Open
Thanukamax wants to merge 1 commit into
mainfrom
feat/addons-config
Open

feat(config): add-ons pricing source of truth#19
Thanukamax wants to merge 1 commit into
mainfrom
feat/addons-config

Conversation

@Thanukamax

@Thanukamax Thanukamax commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

What & why

The onboarding ADD-ONS step is a "0 packs added" placeholder (src/app/choose-modules/page.tsx) — there was no add-on definition anywhere. This adds src/config/addons.tsx as the source of truth for add-ons, the same way plans.tsx is for modules. Marketing (landing-client) should mirror this rather than inventing prices.

Model

  • Per-module add-ons deepen one channel (scope: 'web' | 'cctv' | 'social').
  • Platform add-ons are the cross-module intelligence layer (scope: 'platform') — Cross-Channel Validation, Agent-to-Agent (A2A/MCP), Advanced Insight, Unified Journey, Ask CROW Pro.
  • Pricing mirrors modules: monthly per-month rate, annual = per-month when billed yearly (~17% off, consistent with pricing.ts).
  • icon is a Lucide name string — pure data config, no rendering deps. Helpers getAddOns(scope) and getAddOnPrice(addon, billing).

Catalog (monthly / annual)

Scope Add-on mo yr
CCTV Extra Camera Pack · Queue & Wait-Time · Demographic Insights 20 / 25 / 30 17 / 21 / 25
Web Experiment & A/B · Replay Vault · Warehouse Sync 30 / 20 / 30 25 / 17 / 25
Social Crisis Alerts · Influencer & Reach · Competitor Benchmarking 25 / 25 / 30 21 / 21 / 25
Platform Cross-Channel Validation · A2A · Advanced Insight · Unified Journey · Ask CROW Pro 30 / 40 / 50 / 40 / 20 25 / 33 / 42 / 33 / 17

Notes

  • Prices are a proposal to ratify — adjust to target margins; everything downstream reads from this file.
  • Next: wire choose-modules to render these, and mirror into landing-client's pricing add-ons section.

Summary by CodeRabbit

  • New Features
    • Added support for add-ons pricing and availability information across multiple product areas.
    • Enabled showing eligible add-ons by plan area, including a shared platform category.
    • Added monthly and annual pricing display, with annual pricing normalized to a per-month equivalent.

Define the add-on catalog the onboarding 'ADD-ONS / packs' step was missing
(was a '0 packs added' placeholder). Per-module add-ons (Web/CCTV/Social) plus
the cross-module 'platform' intelligence layer, priced like modules (monthly +
~17%-off annual). Marketing (landing-client) must mirror this — no add-on
prices invented elsewhere.
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

A new src/config/addons.tsx file is added as a pure data module. It defines the AddOnScope type, AddOnConfig interface, and an ADD_ONS catalog array covering CCTV, Web, Social, and Platform scopes with monthly/annual pricing. Two helper functions, getAddOns and getAddOnPrice, are exported.

Add-ons pricing data module

Layer / File(s) Summary
Types, interface, and ADD_ONS catalog
src/config/addons.tsx
Defines AddOnScope type alias and AddOnConfig interface, then populates ADD_ONS with CCTV, Web, Social, and Platform add-ons including metadata, eligibility text, unlocked features, and monthly/annual prices.
getAddOns and getAddOnPrice helpers
src/config/addons.tsx
Adds getAddOns(scope) to filter ADD_ONS by scope and getAddOnPrice(addon, billing) to return the per-month equivalent price for the given billing period.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 A catalog of add-ons, fresh and new,
Scoped by CCTV, Web, and Social too!
Monthly or annual, the price hops right,
getAddOns filters by scope with delight.
The rabbit stamps "merged" and wiggles with glee~ 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding a config source of truth for add-ons pricing.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/addons-config

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

src/config/addons.tsx

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/config/addons.tsx (1)

22-38: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Make the catalog immutable at the module boundary.

Line 38 exports a mutable array of mutable objects, and Line 186 returns those same references. Any consumer can accidentally rewrite a price or requirement and change the shared pricing source for the rest of the session.

Proposed change
 export interface AddOnConfig {
-	id: string;
-	scope: AddOnScope;
-	title: string;
-	description: string;
-	icon: string;
-	price: {
-		monthly: number;
-		annual: number;
+	readonly id: string;
+	readonly scope: AddOnScope;
+	readonly title: string;
+	readonly description: string;
+	readonly icon: string;
+	readonly price: {
+		readonly monthly: number;
+		readonly annual: number;
 	};
 	/** Eligibility / prerequisite note shown under the price. */
-	requirement: string;
+	readonly requirement: string;
 	/** What the add-on unlocks. */
-	unlocks: string[];
+	readonly unlocks: readonly string[];
 }
 
-export const ADD_ONS: AddOnConfig[] = [
+export const ADD_ONS = [
   // ...
-];
+] as const satisfies readonly AddOnConfig[];
 
 /** Add-ons scoped to a given module, or the cross-module "platform" layer. */
-export const getAddOns = (scope: AddOnScope): AddOnConfig[] =>
+export const getAddOns = (scope: AddOnScope): readonly AddOnConfig[] =>
 	ADD_ONS.filter((addon) => addon.scope === scope);

Also applies to: 38-38, 186-187

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/config/addons.tsx` around lines 22 - 38, Make the add-on catalog
immutable at the module boundary by preventing consumers from mutating ADD_ONS
or the objects returned by the helper at the export points in addons.tsx. Update
ADD_ONS and the function that returns catalog entries (the one currently
returning those same references) so callers receive readonly/frozen data or
defensive copies, and ensure the AddOnConfig shape and any related return types
reflect that immutability for fields like price, requirement, and unlocks.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/config/addons.tsx`:
- Around line 22-38: Make the add-on catalog immutable at the module boundary by
preventing consumers from mutating ADD_ONS or the objects returned by the helper
at the export points in addons.tsx. Update ADD_ONS and the function that returns
catalog entries (the one currently returning those same references) so callers
receive readonly/frozen data or defensive copies, and ensure the AddOnConfig
shape and any related return types reflect that immutability for fields like
price, requirement, and unlocks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fad5b8b4-bd29-453f-a56a-fe7be779f257

📥 Commits

Reviewing files that changed from the base of the PR and between bf8960c and db2102e.

📒 Files selected for processing (1)
  • src/config/addons.tsx

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.

1 participant