From 667c92ef073ba1c9ed2e77456a0e1cb6f5ac1e39 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 3 Nov 2025 22:22:32 +0000
Subject: [PATCH 1/5] Initial plan
From 9b4ef58292cb148d120d32ddfaa97f4ddaec852c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 3 Nov 2025 22:42:02 +0000
Subject: [PATCH 2/5] Fix custom plan display showing as Free on account
organizations page
Modified tierToPlan function to lookup custom plans from plansInfo map instead of defaulting to Free. Updated account organizations page to pass plansInfo to tierToPlan calls.
Co-authored-by: stnguyen90 <1477010+stnguyen90@users.noreply.github.com>
---
src/lib/stores/billing.ts | 25 ++++++++++++++++++-
.../account/organizations/+page.svelte | 6 +++--
2 files changed, 28 insertions(+), 3 deletions(-)
diff --git a/src/lib/stores/billing.ts b/src/lib/stores/billing.ts
index 46134dde33..1c4e7323ad 100644
--- a/src/lib/stores/billing.ts
+++ b/src/lib/stores/billing.ts
@@ -85,7 +85,7 @@ export function getRoleLabel(role: string) {
return roles.find((r) => r.value === role)?.label ?? role;
}
-export function tierToPlan(tier: Tier) {
+export function tierToPlan(tier: Tier, plansMap?: PlansMap): TierData {
switch (tier) {
case BillingPlan.FREE:
return tierFree;
@@ -100,6 +100,29 @@ export function tierToPlan(tier: Tier) {
case BillingPlan.ENTERPRISE:
return tierEnterprise;
default:
+ // If plansMap is provided, try to lookup the plan
+ if (plansMap) {
+ const plan = plansMap.get(tier);
+ if (plan) {
+ return {
+ name: plan.name,
+ description: plan.desc
+ };
+ }
+ }
+ // If no plansMap was provided, try to get it from the store
+ if (!plansMap) {
+ const info = get(plansInfo);
+ if (info) {
+ const plan = info.get(tier);
+ if (plan) {
+ return {
+ name: plan.name,
+ description: plan.desc
+ };
+ }
+ }
+ }
return tierFree;
}
}
diff --git a/src/routes/(console)/account/organizations/+page.svelte b/src/routes/(console)/account/organizations/+page.svelte
index a08500bb7d..80640e4896 100644
--- a/src/routes/(console)/account/organizations/+page.svelte
+++ b/src/routes/(console)/account/organizations/+page.svelte
@@ -108,7 +108,8 @@
+ content={tierToPlan(organization?.billingPlan, $plansInfo)
+ ?.name} />
You are limited to 1 free organization per account
@@ -136,7 +137,8 @@
size="xs"
type="success"
variant="secondary"
- content={tierToPlan(payingOrg?.billingPlan)?.name} />
+ content={tierToPlan(payingOrg?.billingPlan, $plansInfo)
+ ?.name} />
{/if}
{/if}
From 56052ddab9a53be56fc498e6b6fd026868b797ec Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 3 Nov 2025 22:49:44 +0000
Subject: [PATCH 3/5] Simplify tierToPlan logic based on code review feedback
Removed redundant conditional and simplified the default case to use a single variable for the map lookup.
Co-authored-by: stnguyen90 <1477010+stnguyen90@users.noreply.github.com>
---
src/lib/stores/billing.ts | 23 ++++++-----------------
1 file changed, 6 insertions(+), 17 deletions(-)
diff --git a/src/lib/stores/billing.ts b/src/lib/stores/billing.ts
index 1c4e7323ad..8391f63e1f 100644
--- a/src/lib/stores/billing.ts
+++ b/src/lib/stores/billing.ts
@@ -99,10 +99,11 @@ export function tierToPlan(tier: Tier, plansMap?: PlansMap): TierData {
return tierCustom;
case BillingPlan.ENTERPRISE:
return tierEnterprise;
- default:
- // If plansMap is provided, try to lookup the plan
- if (plansMap) {
- const plan = plansMap.get(tier);
+ default: {
+ // Try to lookup the plan from provided map or store
+ const map = plansMap || get(plansInfo);
+ if (map) {
+ const plan = map.get(tier);
if (plan) {
return {
name: plan.name,
@@ -110,20 +111,8 @@ export function tierToPlan(tier: Tier, plansMap?: PlansMap): TierData {
};
}
}
- // If no plansMap was provided, try to get it from the store
- if (!plansMap) {
- const info = get(plansInfo);
- if (info) {
- const plan = info.get(tier);
- if (plan) {
- return {
- name: plan.name,
- description: plan.desc
- };
- }
- }
- }
return tierFree;
+ }
}
}
From 17c1d1f8093ec3a07abd1fc2751186674b90801c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 5 Nov 2025 22:53:39 +0000
Subject: [PATCH 4/5] Update tierToPlan to default to tierCustom and fetch
custom plan names from API
- Changed default return value from tierFree to tierCustom for unknown tiers
- Removed optional plansMap parameter and lookup logic from tierToPlan
- Added getPlanName function to fetch custom plan names via billing.getPlan() API
- Updated account organizations page to use async plan name fetching for custom plans
Co-authored-by: stnguyen90 <1477010+stnguyen90@users.noreply.github.com>
---
src/lib/stores/billing.ts | 18 +-----
.../account/organizations/+page.svelte | 61 +++++++++++++------
2 files changed, 46 insertions(+), 33 deletions(-)
diff --git a/src/lib/stores/billing.ts b/src/lib/stores/billing.ts
index 8391f63e1f..db5e28f6b4 100644
--- a/src/lib/stores/billing.ts
+++ b/src/lib/stores/billing.ts
@@ -85,7 +85,7 @@ export function getRoleLabel(role: string) {
return roles.find((r) => r.value === role)?.label ?? role;
}
-export function tierToPlan(tier: Tier, plansMap?: PlansMap): TierData {
+export function tierToPlan(tier: Tier) {
switch (tier) {
case BillingPlan.FREE:
return tierFree;
@@ -99,20 +99,8 @@ export function tierToPlan(tier: Tier, plansMap?: PlansMap): TierData {
return tierCustom;
case BillingPlan.ENTERPRISE:
return tierEnterprise;
- default: {
- // Try to lookup the plan from provided map or store
- const map = plansMap || get(plansInfo);
- if (map) {
- const plan = map.get(tier);
- if (plan) {
- return {
- name: plan.name,
- description: plan.desc
- };
- }
- }
- return tierFree;
- }
+ default:
+ return tierCustom;
}
}
diff --git a/src/routes/(console)/account/organizations/+page.svelte b/src/routes/(console)/account/organizations/+page.svelte
index 80640e4896..db917ea6d2 100644
--- a/src/routes/(console)/account/organizations/+page.svelte
+++ b/src/routes/(console)/account/organizations/+page.svelte
@@ -16,7 +16,7 @@
import { Badge } from '@appwrite.io/pink-svelte';
import type { Models } from '@appwrite.io/console';
import type { Organization } from '$lib/stores/organization';
- import { daysLeftInTrial, plansInfo, tierToPlan } from '$lib/stores/billing';
+ import { daysLeftInTrial, plansInfo, tierToPlan, type Tier } from '$lib/stores/billing';
import { toLocaleDate } from '$lib/helpers/date';
import { BillingPlan } from '$lib/constants';
import { goto } from '$app/navigation';
@@ -36,6 +36,27 @@
return memberships.memberships.map((team) => team.userName || team.userEmail);
}
+ async function getPlanName(billingPlan: string | undefined): Promise {
+ if (!billingPlan) return 'Unknown';
+
+ // For known plans, use tierToPlan
+ const tierData = tierToPlan(billingPlan as Tier);
+
+ // If it's not a custom plan or we got a non-custom result, return the name
+ if (tierData.name !== 'Custom') {
+ return tierData.name;
+ }
+
+ // For custom plans, fetch from API
+ try {
+ const plan = await sdk.forConsole.billing.getPlan(billingPlan);
+ return plan.name;
+ } catch (error) {
+ // Fallback to 'Custom' if fetch fails
+ return 'Custom';
+ }
+ }
+
function isOrganizationOnTrial(organization: Organization): boolean {
if (!organization?.billingTrialStartDate) return false;
if ($daysLeftInTrial <= 0) return false;
@@ -92,6 +113,9 @@
{#each data.organizations.teams as organization}
{@const avatarList = getMemberships(organization.$id)}
{@const payingOrg = isPayingOrganization(organization)}
+ {@const planName = isCloudOrg(organization)
+ ? getPlanName(organization.billingPlan)
+ : null}
@@ -104,17 +128,17 @@
{#if isCloudOrg(organization)}
{#if isNonPayingOrganization(organization)}
-
-
-
-
- You are limited to 1 free organization per account
-
-
+ {#if planName}
+ {#await planName then name}
+
+
+
+
+ You are limited to 1 free organization per account
+
+
+ {/await}
+ {/if}
{/if}
{#if isOrganizationOnTrial(organization)}
@@ -133,12 +157,13 @@
{/if}
{#if payingOrg}
-
+ {#await getPlanName(payingOrg.billingPlan) then name}
+
+ {/await}
{/if}
{/if}
From b445fad76f3c77d96a9f42ec9b2469a01377b269 Mon Sep 17 00:00:00 2001
From: Steven Nguyen
Date: Wed, 12 Nov 2025 13:55:30 -0800
Subject: [PATCH 5/5] chore: show skeleton loaders while loading organization
plan names
---
.../(console)/account/organizations/+page.svelte | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/src/routes/(console)/account/organizations/+page.svelte b/src/routes/(console)/account/organizations/+page.svelte
index db917ea6d2..2d16680439 100644
--- a/src/routes/(console)/account/organizations/+page.svelte
+++ b/src/routes/(console)/account/organizations/+page.svelte
@@ -13,7 +13,7 @@
import { sdk } from '$lib/stores/sdk';
import type { PageData } from './$types';
import { isCloud } from '$lib/system';
- import { Badge } from '@appwrite.io/pink-svelte';
+ import { Badge, Skeleton } from '@appwrite.io/pink-svelte';
import type { Models } from '@appwrite.io/console';
import type { Organization } from '$lib/stores/organization';
import { daysLeftInTrial, plansInfo, tierToPlan, type Tier } from '$lib/stores/billing';
@@ -129,7 +129,9 @@
{#if isCloudOrg(organization)}
{#if isNonPayingOrganization(organization)}
{#if planName}
- {#await planName then name}
+ {#await planName}
+
+ {:then name}
@@ -157,7 +159,9 @@
{/if}
{#if payingOrg}
- {#await getPlanName(payingOrg.billingPlan) then name}
+ {#await planName}
+
+ {:then name}
{#await avatarList}
-
+
{:then avatars}
{/await}