Skip to content

Fix: disable free plan in selection if one already exists#2662

Merged
ItzNotABug merged 1 commit intomainfrom
disable-free-when-applicable
Dec 2, 2025
Merged

Fix: disable free plan in selection if one already exists#2662
ItzNotABug merged 1 commit intomainfrom
disable-free-when-applicable

Conversation

@ItzNotABug
Copy link
Copy Markdown
Contributor

@ItzNotABug ItzNotABug commented Dec 2, 2025

What does this PR do?

Mark Free as disabled when one already exists on an account.

Test Plan

Manual.

Screenshot 2025-12-02 at 2 21 36 PM

Related PRs and Issues

N/A.

Have you read the Contributing Guidelines on issues?

Yes.

Summary by CodeRabbit

  • New Features
    • Added tooltip on plan selection indicating that only one free organization is allowed per account, improving clarity for users during organization setup and plan changes.

✏️ Tip: You can customize this high-level summary in your review settings.

@ItzNotABug ItzNotABug self-assigned this Dec 2, 2025
@appwrite
Copy link
Copy Markdown

appwrite Bot commented Dec 2, 2025

Console (appwrite/console)

Project ID: 688b7bf400350cbd60e9

Sites (1)
Site Status Logs Preview QR
 console-stage
688b7cf6003b1842c9dc
Failed Failed View Logs Preview URL QR Code

Tip

SSR frameworks are fully supported with configurable build runtimes

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 2, 2025

Walkthrough

This change set refactors the billing plan selection workflow to introduce free organization tracking and modernize reactive patterns. The PlanSelection component gains a Tooltip that displays "Only 1 free organization is allowed per account" using a new shouldShowTooltip helper and accepts a new anyOrgFree boolean prop. Two page components (+page.svelte files) transition from legacy Svelte export patterns to Svelte 5's $props() and $state() syntax, wrapping reactive variables in $state() decorators. The anyOrgFree flag propagates through the component hierarchy from page data to inform plan selection behavior. A coupon fetch handler is simplified by removing an intermediate response variable.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • PlanSelection component API change: New anyOrgFree prop and Tooltip wrapper require verification that tooltip display logic in shouldShowTooltip correctly gates the free organization message and integrates properly with existing plan rendering
  • Svelte 5 reactive pattern migration: Two page components convert from export let to $props() destructuring and plain variables to $state() — review the initialization of all reactive stores (showExitModal, selectedPlan, formComponent, billingBudget, etc.) to ensure they maintain intended reactivity and nullability
  • Prop propagation: Verify anyOrgFree flows correctly from data through PlanSelection in both create-organization and change-plan pages
  • Behavioral preservation: Confirm that state management refactoring (especially the simplified coupon fetch and previousPage resolve usage) preserves existing form submission and navigation logic

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix: disable free plan in selection if one already exists' clearly and specifically describes the main change: disabling the free plan option when one already exists on the account.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch disable-free-when-applicable

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
Copy Markdown
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: 2

🧹 Nitpick comments (1)
src/lib/components/billing/planSelection.svelte (1)

21-24: Consider renaming for clarity.

The function name shouldShowTooltip is misleading because it actually returns whether to disable the tooltip (since it's used with disabled={shouldShowTooltip(plan)}). While the logic is correct, this could confuse future maintainers.

Consider renaming to better reflect its purpose:

-function shouldShowTooltip(plan: Plan) {
+function shouldDisableTooltip(plan: Plan) {
     if (plan.$id !== BillingPlan.FREE) return true;
     else return !anyOrgFree;
 }

Or inverting the logic with a clearer name:

-function shouldShowTooltip(plan: Plan) {
-    if (plan.$id !== BillingPlan.FREE) return true;
-    else return !anyOrgFree;
+function shouldShowFreeTooltip(plan: Plan) {
+    return plan.$id === BillingPlan.FREE && anyOrgFree;
 }

Then update line 29:

-<Tooltip disabled={shouldShowTooltip(plan)} maxWidth="fit-content">
+<Tooltip disabled={!shouldShowFreeTooltip(plan)} maxWidth="fit-content">
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f6b29f2 and fe7fdeb.

📒 Files selected for processing (3)
  • src/lib/components/billing/planSelection.svelte (2 hunks)
  • src/routes/(console)/create-organization/+page.svelte (4 hunks)
  • src/routes/(console)/organization-[organization]/change-plan/+page.svelte (1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx,js,jsx,svelte}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx,svelte}: Import reusable modules from the src/lib directory using the $lib alias
Use minimal comments in code; reserve comments for TODOs or complex logic explanations
Use $lib, $routes, and $themes aliases instead of relative paths for module imports

Files:

  • src/routes/(console)/create-organization/+page.svelte
  • src/routes/(console)/organization-[organization]/change-plan/+page.svelte
  • src/lib/components/billing/planSelection.svelte
src/routes/**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Use SvelteKit file conventions: +page.svelte for components, +page.ts for data loaders, +layout.svelte for wrappers, +error.svelte for error handling, and dynamic route params in square brackets like [param]

Files:

  • src/routes/(console)/create-organization/+page.svelte
  • src/routes/(console)/organization-[organization]/change-plan/+page.svelte
**/*.{ts,tsx,js,jsx,svelte,json}

📄 CodeRabbit inference engine (AGENTS.md)

Use 4 spaces for indentation, single quotes, 100 character line width, and no trailing commas per Prettier configuration

Files:

  • src/routes/(console)/create-organization/+page.svelte
  • src/routes/(console)/organization-[organization]/change-plan/+page.svelte
  • src/lib/components/billing/planSelection.svelte
**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Use Svelte 5 + SvelteKit 2 syntax with TypeScript for component development

Files:

  • src/routes/(console)/create-organization/+page.svelte
  • src/routes/(console)/organization-[organization]/change-plan/+page.svelte
  • src/lib/components/billing/planSelection.svelte
src/routes/**

📄 CodeRabbit inference engine (AGENTS.md)

Configure dynamic routes using SvelteKit convention with [param] syntax in route directory names

Files:

  • src/routes/(console)/create-organization/+page.svelte
  • src/routes/(console)/organization-[organization]/change-plan/+page.svelte
src/lib/components/**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Use PascalCase for component file names and place them in src/lib/components/[feature]/ directory structure

Files:

  • src/lib/components/billing/planSelection.svelte
🧠 Learnings (6)
📚 Learning: 2025-11-25T03:15:27.539Z
Learnt from: CR
Repo: appwrite/console PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T03:15:27.539Z
Learning: Applies to src/routes/**/*.svelte : Use SvelteKit file conventions: +page.svelte for components, +page.ts for data loaders, +layout.svelte for wrappers, +error.svelte for error handling, and dynamic route params in square brackets like [param]

Applied to files:

  • src/routes/(console)/create-organization/+page.svelte
📚 Learning: 2025-10-05T09:41:40.439Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2398
File: src/routes/(console)/verify-email/+page.svelte:48-51
Timestamp: 2025-10-05T09:41:40.439Z
Learning: In SvelteKit 5, `page` imported from `$app/state` is a reactive state object (using runes), not a store. It should be accessed as `page.data` without the `$` prefix, unlike the store-based `$page` from `$app/stores` in earlier versions.

Applied to files:

  • src/routes/(console)/create-organization/+page.svelte
📚 Learning: 2025-10-13T05:13:54.542Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2413
File: src/routes/(console)/project-[region]-[project]/databases/table.svelte:33-39
Timestamp: 2025-10-13T05:13:54.542Z
Learning: In Svelte 5, `import { page } from '$app/state'` provides a reactive state proxy that can be accessed directly (e.g., `page.params`), unlike the older `import { page } from '$app/stores'` which returns a readable store requiring the `$page` syntax for auto-subscription in components.

Applied to files:

  • src/routes/(console)/create-organization/+page.svelte
📚 Learning: 2025-11-25T03:15:27.539Z
Learnt from: CR
Repo: appwrite/console PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T03:15:27.539Z
Learning: Applies to src/lib/components/**/*.svelte : Use PascalCase for component file names and place them in src/lib/components/[feature]/ directory structure

Applied to files:

  • src/routes/(console)/create-organization/+page.svelte
📚 Learning: 2025-11-25T03:15:27.539Z
Learnt from: CR
Repo: appwrite/console PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T03:15:27.539Z
Learning: Applies to **/*.{ts,tsx,js,jsx,svelte} : Use $lib, $routes, and $themes aliases instead of relative paths for module imports

Applied to files:

  • src/routes/(console)/create-organization/+page.svelte
📚 Learning: 2025-10-13T05:16:07.656Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2413
File: src/routes/(console)/project-[region]-[project]/databases/database-[database]/header.svelte:54-58
Timestamp: 2025-10-13T05:16:07.656Z
Learning: In SvelteKit apps, shared layout components (like headers) that use `$derived(page.data.*)` should use optional chaining when accessing properties that may not be present on all routes. During page transitions, reactive statements can briefly evaluate with different page.data structures, so optional chaining prevents runtime errors when navigating between routes with different data shapes (e.g., between `/databases` and `/databases/database-[database]`).

Applied to files:

  • src/routes/(console)/create-organization/+page.svelte
🔇 Additional comments (8)
src/routes/(console)/organization-[organization]/change-plan/+page.svelte (2)

327-330: LGTM: Clean integration of free organization check.

The new anyOrgFree prop correctly propagates the free organization status to the plan selection component, enabling the tooltip and disable logic.


297-300: Good defensive logic for preventing invalid downgrades.

The button disable condition correctly prevents downgrading to the Free plan when a free organization already exists, providing a second layer of protection beyond the UI-level disabling.

src/lib/components/billing/planSelection.svelte (3)

33-33: Correct disable logic for free plan restriction.

The disabled condition properly prevents selection of the Free plan when a free organization already exists, which aligns with the PR objective.


52-54: Clear and helpful tooltip message.

The tooltip content effectively communicates the restriction to users in plain language.


34-34: Verify if tooltipShow prop is still needed on LabelCard.

The tooltipShow prop on LabelCard may be redundant now that the card is wrapped in a Tooltip component. Confirm whether LabelCard uses this prop internally or if it can be safely removed to avoid duplicate tooltip behavior.

src/routes/(console)/create-organization/+page.svelte (3)

49-59: Good simplification of coupon fetch logic.

Removing the intermediate variable makes the code more concise without affecting functionality.


25-42: Svelte 5 migration follows correct patterns (with one exception).

The migration to $props() and $state() follows Svelte 5 conventions correctly, with proper TypeScript typing. However, see the critical issue on line 32 regarding isSubmitting.


28-28: No action needed—resolve('/(console)') is the appropriate choice.

Using resolve() for navigation initialization is correct. It resolves app-root paths through SvelteKit's navigation system, unlike base which is a static asset prefix. These serve different purposes and are not interchangeable.

Comment thread src/routes/(console)/create-organization/+page.svelte
Comment thread src/routes/(console)/create-organization/+page.svelte
@ItzNotABug ItzNotABug merged commit b3b19df into main Dec 2, 2025
3 of 4 checks passed
@ItzNotABug ItzNotABug deleted the disable-free-when-applicable branch December 2, 2025 11:23
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