From c67fea49824e3c541c46aeaf578a4b81b348f84e Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 28 Aug 2024 02:57:28 +0000 Subject: [PATCH 01/39] update create --- src/lib/sdk/billing.ts | 8 ++- .../console/create-organization/+page.svelte | 51 ++++++++----------- 2 files changed, 27 insertions(+), 32 deletions(-) diff --git a/src/lib/sdk/billing.ts b/src/lib/sdk/billing.ts index f0fd8ec089..ebe2a55571 100644 --- a/src/lib/sdk/billing.ts +++ b/src/lib/sdk/billing.ts @@ -307,7 +307,9 @@ export class Billing { name: string, billingPlan: string, paymentMethodId: string, - billingAddressId: string = undefined + billingAddressId: string = undefined, + couponId: string = undefined, + invites: Array = [] ): Promise { const path = `/organizations`; const params = { @@ -315,7 +317,9 @@ export class Billing { name, billingPlan, paymentMethodId, - billingAddressId + billingAddressId, + couponId, + invites }; const uri = new URL(this.client.config.endpoint + path); return await this.client.call( diff --git a/src/routes/console/create-organization/+page.svelte b/src/routes/console/create-organization/+page.svelte index e7ac26659d..1b9dd8b458 100644 --- a/src/routes/console/create-organization/+page.svelte +++ b/src/routes/console/create-organization/+page.svelte @@ -103,23 +103,14 @@ null ); } else { - // Create free organization if coming from onboarding - if (previousPage.includes('/console/onboarding') && !anyOrgFree) { - await sdk.forConsole.billing.createOrganization( - ID.unique(), - 'Personal Projects', - BillingPlan.FREE, - null, - null - ); - } - org = await sdk.forConsole.billing.createOrganization( ID.unique(), name, billingPlan, paymentMethodId, - null + null, + couponData?.code, + collaborators ); //Add budget @@ -127,25 +118,25 @@ await sdk.forConsole.billing.updateBudget(org.$id, billingBudget, [75]); } - //Add coupon - if (couponData?.code) { - await sdk.forConsole.billing.addCredit(org.$id, couponData.code); - trackEvent(Submit.CreditRedeem); - } + // //Add coupon + // if (couponData?.code) { + // await sdk.forConsole.billing.addCredit(org.$id, couponData.code); + // trackEvent(Submit.CreditRedeem); + // } - //Add collaborators - if (collaborators?.length) { - collaborators.forEach(async (collaborator) => { - await sdk.forConsole.teams.createMembership( - org.$id, - ['owner'], - collaborator, - undefined, - undefined, - `${$page.url.origin}/console/organization-${org.$id}` - ); - }); - } + // //Add collaborators + // if (collaborators?.length) { + // collaborators.forEach(async (collaborator) => { + // await sdk.forConsole.teams.createMembership( + // org.$id, + // ['owner'], + // collaborator, + // undefined, + // undefined, + // `${$page.url.origin}/console/organization-${org.$id}` + // ); + // }); + // } // Add tax ID if (taxId) { From 708517a0d075ee90e7706770e388a455b5635ae5 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 28 Aug 2024 11:08:18 +0545 Subject: [PATCH 02/39] authorize payment --- .../commandCenter/searchers/organizations.ts | 2 +- src/lib/sdk/billing.ts | 4 ++-- src/lib/stores/organization.ts | 9 ++++++++ src/routes/+layout.ts | 2 +- .../console/account/organizations/+page.ts | 2 +- .../console/create-organization/+page.svelte | 21 ++++++++++++++++--- 6 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/lib/commandCenter/searchers/organizations.ts b/src/lib/commandCenter/searchers/organizations.ts index df72d77b26..e5e8d76940 100644 --- a/src/lib/commandCenter/searchers/organizations.ts +++ b/src/lib/commandCenter/searchers/organizations.ts @@ -3,7 +3,7 @@ import { sdk } from '$lib/stores/sdk'; import type { Searcher } from '../commands'; export const orgSearcher = (async (query: string) => { - const { teams } = await sdk.forConsole.teams.list(); + const { teams } = await sdk.forConsole.billing.listOrganization(); return teams .filter((organization) => organization.name.toLowerCase().includes(query.toLowerCase())) .map((organization) => { diff --git a/src/lib/sdk/billing.ts b/src/lib/sdk/billing.ts index ebe2a55571..ea278c5bac 100644 --- a/src/lib/sdk/billing.ts +++ b/src/lib/sdk/billing.ts @@ -1,5 +1,5 @@ import type { Client, Models } from '@appwrite.io/console'; -import type { Organization, OrganizationList } from '../stores/organization'; +import type { CreateOrgAuth, Organization, OrganizationList } from '../stores/organization'; import type { PaymentMethod } from '@stripe/stripe-js'; import type { Tier } from '$lib/stores/billing'; @@ -310,7 +310,7 @@ export class Billing { billingAddressId: string = undefined, couponId: string = undefined, invites: Array = [] - ): Promise { + ): Promise { const path = `/organizations`; const params = { organizationId, diff --git a/src/lib/stores/organization.ts b/src/lib/stores/organization.ts index 2beb13b03c..d41cc3f54a 100644 --- a/src/lib/stores/organization.ts +++ b/src/lib/stores/organization.ts @@ -3,6 +3,15 @@ import { derived, writable } from 'svelte/store'; import type { Models } from '@appwrite.io/console'; import type { Tier } from './billing'; +export type CreateOrgAuth = { + status: number; + message: string; + teamId: string; + invoiceId: string; + clientSecret: string; + type: string; +}; + export type Organization = Models.Team> & { billingBudget: number; billingPlan: Tier; diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts index 85ad4e83d9..5f0472229f 100644 --- a/src/routes/+layout.ts +++ b/src/routes/+layout.ts @@ -22,7 +22,7 @@ export const load: LayoutLoad = async ({ depends, url }) => { return { account, - organizations: await sdk.forConsole.teams.list() + organizations: await sdk.forConsole.billing.listOrganization() }; } catch (error) { const acceptedRoutes = [ diff --git a/src/routes/console/account/organizations/+page.ts b/src/routes/console/account/organizations/+page.ts index 0b89b1d302..3f21115b89 100644 --- a/src/routes/console/account/organizations/+page.ts +++ b/src/routes/console/account/organizations/+page.ts @@ -12,7 +12,7 @@ export const load: PageLoad = async ({ url, route }) => { return { offset, limit, - organizations: await sdk.forConsole.teams.list([ + organizations: await sdk.forConsole.billing.listOrganization([ Query.offset(offset), Query.limit(limit), Query.orderDesc('') diff --git a/src/routes/console/create-organization/+page.svelte b/src/routes/console/create-organization/+page.svelte index 1b9dd8b458..3bcbae0413 100644 --- a/src/routes/console/create-organization/+page.svelte +++ b/src/routes/console/create-organization/+page.svelte @@ -22,8 +22,13 @@ import type { Coupon, PaymentList } from '$lib/sdk/billing'; import { plansInfo, tierFree, tierPro, tierToPlan } from '$lib/stores/billing'; import { addNotification } from '$lib/stores/notifications'; - import { organizationList, type Organization } from '$lib/stores/organization'; + import { + organizationList, + type CreateOrgAuth, + type Organization + } from '$lib/stores/organization'; import { sdk } from '$lib/stores/sdk'; + import { confirmPayment } from '$lib/stores/stripe'; import { ID } from '@appwrite.io/console'; import { onMount } from 'svelte'; import { writable } from 'svelte/store'; @@ -90,9 +95,13 @@ paymentMethodId = methods.paymentMethods.find((method) => !!method?.last4)?.$id ?? null; } + function isOrganization(org: Organization | CreateOrgAuth): org is Organization { + return (org as Organization).$id !== undefined; + } + async function create() { try { - let org: Organization; + let org: Organization | CreateOrgAuth; if (billingPlan === BillingPlan.FREE) { org = await sdk.forConsole.billing.createOrganization( @@ -113,8 +122,14 @@ collaborators ); + if (!isOrganization(org) && org.status == 402) { + let clientSecret = org.clientSecret; + await confirmPayment('', clientSecret, paymentMethodId); + // throw new Error('Payment requires authentication'); + } + //Add budget - if (billingBudget) { + if (billingBudget && isOrganization(org)) { await sdk.forConsole.billing.updateBudget(org.$id, billingBudget, [75]); } From eec5c6f1f34b5fb5e0d788166a3e92eae9819ef5 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Thu, 29 Aug 2024 08:30:49 +0545 Subject: [PATCH 03/39] support taxid and budget while creating --- src/lib/sdk/billing.ts | 8 ++++++-- .../console/create-organization/+page.svelte | 16 +++++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/lib/sdk/billing.ts b/src/lib/sdk/billing.ts index ea278c5bac..936d9f3ad8 100644 --- a/src/lib/sdk/billing.ts +++ b/src/lib/sdk/billing.ts @@ -309,7 +309,9 @@ export class Billing { paymentMethodId: string, billingAddressId: string = undefined, couponId: string = undefined, - invites: Array = [] + invites: Array = [], + budget: number = undefined, + taxId: string = undefined ): Promise { const path = `/organizations`; const params = { @@ -319,7 +321,9 @@ export class Billing { paymentMethodId, billingAddressId, couponId, - invites + invites, + budget, + taxId }; const uri = new URL(this.client.config.endpoint + path); return await this.client.call( diff --git a/src/routes/console/create-organization/+page.svelte b/src/routes/console/create-organization/+page.svelte index 3bcbae0413..09c9e9248b 100644 --- a/src/routes/console/create-organization/+page.svelte +++ b/src/routes/console/create-organization/+page.svelte @@ -119,7 +119,9 @@ paymentMethodId, null, couponData?.code, - collaborators + collaborators, + billingBudget, + taxId ); if (!isOrganization(org) && org.status == 402) { @@ -129,9 +131,9 @@ } //Add budget - if (billingBudget && isOrganization(org)) { - await sdk.forConsole.billing.updateBudget(org.$id, billingBudget, [75]); - } + // if (billingBudget && isOrganization(org)) { + // await sdk.forConsole.billing.updateBudget(org.$id, billingBudget, [75]); + // } // //Add coupon // if (couponData?.code) { @@ -154,9 +156,9 @@ // } // Add tax ID - if (taxId) { - await sdk.forConsole.billing.updateTaxId(org.$id, taxId); - } + // if (taxId) { + // await sdk.forConsole.billing.updateTaxId(org.$id, taxId); + // } } trackEvent(Submit.OrganizationCreate, { From 414bc60bc88ef93dd87c4023dde3e7e55898c0b1 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Thu, 29 Aug 2024 11:26:22 +0545 Subject: [PATCH 04/39] handle validation --- src/lib/sdk/billing.ts | 26 ++++++++- src/lib/stores/stripe.ts | 10 +++- .../console/create-organization/+page.svelte | 56 ++++++++++++++++--- .../billing/paymentHistory.svelte | 1 - 4 files changed, 77 insertions(+), 16 deletions(-) diff --git a/src/lib/sdk/billing.ts b/src/lib/sdk/billing.ts index 936d9f3ad8..646fdc1752 100644 --- a/src/lib/sdk/billing.ts +++ b/src/lib/sdk/billing.ts @@ -302,16 +302,36 @@ export class Billing { ); } + async validateOrganization( + organizationId: string, + invites: string[], + ): Promise { + const path = `/organizations/${organizationId}/validate`; + const params = { + organizationId, + invites, + }; + const uri = new URL(this.client.config.endpoint + path); + return await this.client.call( + 'PATCH', + uri, + { + 'content-type': 'application/json' + }, + params + ); + } + async createOrganization( organizationId: string, name: string, billingPlan: string, paymentMethodId: string, - billingAddressId: string = undefined, - couponId: string = undefined, + billingAddressId: string = null, + couponId: string = null, invites: Array = [], budget: number = undefined, - taxId: string = undefined + taxId: string = null ): Promise { const path = `/organizations`; const params = { diff --git a/src/lib/stores/stripe.ts b/src/lib/stores/stripe.ts index 93b4576aec..b1f01016b8 100644 --- a/src/lib/stores/stripe.ts +++ b/src/lib/stores/stripe.ts @@ -116,7 +116,8 @@ export async function confirmPayment( orgId: string, clientSecret: string, paymentMethodId: string, - route?: string + route?: string, + returnError: boolean = false ) { try { const url = @@ -128,10 +129,13 @@ export async function confirmPayment( clientSecret: clientSecret, confirmParams: { return_url: url, - payment_method: paymentMethod.providerMethodId - } + payment_method: paymentMethod.providerMethodId, + }, }); if (error) { + if (returnError) { + return error; + } throw error.message; } } catch (e) { diff --git a/src/routes/console/create-organization/+page.svelte b/src/routes/console/create-organization/+page.svelte index 09c9e9248b..67d1341f15 100644 --- a/src/routes/console/create-organization/+page.svelte +++ b/src/routes/console/create-organization/+page.svelte @@ -88,6 +88,14 @@ if (anyOrgFree) { billingPlan = BillingPlan.PRO; } + if($page.url.searchParams.has('type')) { + const type = $page.url.searchParams.get('type'); + if(type === 'confirmed') { + const organizationId= $page.url.searchParams.get('id'); + const invites = $page.url.searchParams.getAll('invites'); + await validate(organizationId, invites); + } + } }); async function loadPaymentMethods() { @@ -99,6 +107,27 @@ return (org as Organization).$id !== undefined; } + async function validate(organizationId: string, invites: string[]) { + try { + let org = await sdk.forConsole.billing.validateOrganization(organizationId, invites); + if (isOrganization(org)) { + await preloadData(`${base}/console/organization-${org.$id}`); + await goto(`${base}/console/organization-${org.$id}`); + addNotification({ + type: 'success', + message: `${org.name ?? 'Organization'} has been created` + }); + } + } catch (e) { + addNotification({ + type: 'error', + message: e.message + }); + trackError(e, Submit.OrganizationCreate); + } + + } + async function create() { try { let org: Organization | CreateOrgAuth; @@ -126,8 +155,15 @@ if (!isOrganization(org) && org.status == 402) { let clientSecret = org.clientSecret; - await confirmPayment('', clientSecret, paymentMethodId); - // throw new Error('Payment requires authentication'); + let params = new URLSearchParams(); + params.append('type', 'confirmed'); + params.append('id', org.teamId); + for (let index = 0; index < collaborators.length; index++) { + const invite = collaborators[index]; + params.append('invites', invite); + } + await confirmPayment('', clientSecret, paymentMethodId, '/console/create-organization?'+params.toString()); + await validate(org.teamId, collaborators); } //Add budget @@ -167,13 +203,15 @@ members_invited: collaborators?.length }); - await invalidate(Dependencies.ACCOUNT); - await preloadData(`${base}/console/organization-${org.$id}`); - await goto(`${base}/console/organization-${org.$id}`); - addNotification({ - type: 'success', - message: `${name ?? 'Organization'} has been created` - }); + if(isOrganization(org)) { + await invalidate(Dependencies.ACCOUNT); + await preloadData(`${base}/console/organization-${org.$id}`); + await goto(`${base}/console/organization-${org.$id}`); + addNotification({ + type: 'success', + message: `${org.name ?? 'Organization'} has been created` + }); + } } catch (e) { addNotification({ type: 'error', diff --git a/src/routes/console/organization-[organization]/billing/paymentHistory.svelte b/src/routes/console/organization-[organization]/billing/paymentHistory.svelte index 1784b0a57d..0c9642dfca 100644 --- a/src/routes/console/organization-[organization]/billing/paymentHistory.svelte +++ b/src/routes/console/organization-[organization]/billing/paymentHistory.svelte @@ -49,7 +49,6 @@ invoiceList = await sdk.forConsole.billing.listInvoices($page.params.organization, [ Query.limit(limit), Query.offset(offset), - Query.notEqual('from', $organization.billingCurrentInvoiceDate), Query.orderDesc('$createdAt') ]); } From c97e372386764d6cd456e9cf776368f1c4f3fa26 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Tue, 3 Sep 2024 09:17:18 +0545 Subject: [PATCH 05/39] fix check with cloud --- src/lib/sdk/billing.ts | 4 +- src/lib/stores/organization.ts | 2 +- src/routes/+layout.ts | 3 +- .../console/account/organizations/+page.ts | 15 ++++-- .../console/create-organization/+page.svelte | 52 +++++-------------- 5 files changed, 28 insertions(+), 48 deletions(-) diff --git a/src/lib/sdk/billing.ts b/src/lib/sdk/billing.ts index 646fdc1752..3c93d32aec 100644 --- a/src/lib/sdk/billing.ts +++ b/src/lib/sdk/billing.ts @@ -1,5 +1,5 @@ import type { Client, Models } from '@appwrite.io/console'; -import type { CreateOrgAuth, Organization, OrganizationList } from '../stores/organization'; +import type { OrganizationError, Organization, OrganizationList } from '../stores/organization'; import type { PaymentMethod } from '@stripe/stripe-js'; import type { Tier } from '$lib/stores/billing'; @@ -332,7 +332,7 @@ export class Billing { invites: Array = [], budget: number = undefined, taxId: string = null - ): Promise { + ): Promise { const path = `/organizations`; const params = { organizationId, diff --git a/src/lib/stores/organization.ts b/src/lib/stores/organization.ts index d41cc3f54a..ca7269ca37 100644 --- a/src/lib/stores/organization.ts +++ b/src/lib/stores/organization.ts @@ -3,7 +3,7 @@ import { derived, writable } from 'svelte/store'; import type { Models } from '@appwrite.io/console'; import type { Tier } from './billing'; -export type CreateOrgAuth = { +export type OrganizationError = { status: number; message: string; teamId: string; diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts index 5f0472229f..8986f1a126 100644 --- a/src/routes/+layout.ts +++ b/src/routes/+layout.ts @@ -6,6 +6,7 @@ import { redirect } from '@sveltejs/kit'; import { Dependencies } from '$lib/constants'; import type { LayoutLoad } from './$types'; import { redirectTo } from './store'; +import { isCloud } from '$lib/system'; export const ssr = false; @@ -22,7 +23,7 @@ export const load: LayoutLoad = async ({ depends, url }) => { return { account, - organizations: await sdk.forConsole.billing.listOrganization() + organizations: isCloud ? await sdk.forConsole.billing.listOrganization() : await sdk.forConsole.teams.list() }; } catch (error) { const acceptedRoutes = [ diff --git a/src/routes/console/account/organizations/+page.ts b/src/routes/console/account/organizations/+page.ts index 3f21115b89..803497c6a8 100644 --- a/src/routes/console/account/organizations/+page.ts +++ b/src/routes/console/account/organizations/+page.ts @@ -3,19 +3,24 @@ import { sdk } from '$lib/stores/sdk'; import { getLimit, getPage, pageToOffset } from '$lib/helpers/load'; import { CARD_LIMIT } from '$lib/constants'; import type { PageLoad } from './$types'; +import { isCloud } from '$lib/system'; export const load: PageLoad = async ({ url, route }) => { const page = getPage(url); const limit = getLimit(url, route, CARD_LIMIT); const offset = pageToOffset(page, limit); + const queries = [ + Query.offset(offset), + Query.limit(limit), + Query.orderDesc('') + ]; + + const organizations = isCloud ? await sdk.forConsole.billing.listOrganization(queries) : sdk.forConsole.teams.list(queries); + return { offset, limit, - organizations: await sdk.forConsole.billing.listOrganization([ - Query.offset(offset), - Query.limit(limit), - Query.orderDesc('') - ]) + organizations }; }; diff --git a/src/routes/console/create-organization/+page.svelte b/src/routes/console/create-organization/+page.svelte index 67d1341f15..323d48c610 100644 --- a/src/routes/console/create-organization/+page.svelte +++ b/src/routes/console/create-organization/+page.svelte @@ -24,7 +24,7 @@ import { addNotification } from '$lib/stores/notifications'; import { organizationList, - type CreateOrgAuth, + type OrganizationError, type Organization } from '$lib/stores/organization'; import { sdk } from '$lib/stores/sdk'; @@ -88,10 +88,10 @@ if (anyOrgFree) { billingPlan = BillingPlan.PRO; } - if($page.url.searchParams.has('type')) { + if ($page.url.searchParams.has('type')) { const type = $page.url.searchParams.get('type'); - if(type === 'confirmed') { - const organizationId= $page.url.searchParams.get('id'); + if (type === 'confirmed') { + const organizationId = $page.url.searchParams.get('id'); const invites = $page.url.searchParams.getAll('invites'); await validate(organizationId, invites); } @@ -103,7 +103,7 @@ paymentMethodId = methods.paymentMethods.find((method) => !!method?.last4)?.$id ?? null; } - function isOrganization(org: Organization | CreateOrgAuth): org is Organization { + function isOrganization(org: Organization | OrganizationError): org is Organization { return (org as Organization).$id !== undefined; } @@ -125,12 +125,11 @@ }); trackError(e, Submit.OrganizationCreate); } - } async function create() { try { - let org: Organization | CreateOrgAuth; + let org: Organization | OrganizationError; if (billingPlan === BillingPlan.FREE) { org = await sdk.forConsole.billing.createOrganization( @@ -162,39 +161,14 @@ const invite = collaborators[index]; params.append('invites', invite); } - await confirmPayment('', clientSecret, paymentMethodId, '/console/create-organization?'+params.toString()); + await confirmPayment( + '', + clientSecret, + paymentMethodId, + '/console/create-organization?' + params.toString() + ); await validate(org.teamId, collaborators); } - - //Add budget - // if (billingBudget && isOrganization(org)) { - // await sdk.forConsole.billing.updateBudget(org.$id, billingBudget, [75]); - // } - - // //Add coupon - // if (couponData?.code) { - // await sdk.forConsole.billing.addCredit(org.$id, couponData.code); - // trackEvent(Submit.CreditRedeem); - // } - - // //Add collaborators - // if (collaborators?.length) { - // collaborators.forEach(async (collaborator) => { - // await sdk.forConsole.teams.createMembership( - // org.$id, - // ['owner'], - // collaborator, - // undefined, - // undefined, - // `${$page.url.origin}/console/organization-${org.$id}` - // ); - // }); - // } - - // Add tax ID - // if (taxId) { - // await sdk.forConsole.billing.updateTaxId(org.$id, taxId); - // } } trackEvent(Submit.OrganizationCreate, { @@ -203,7 +177,7 @@ members_invited: collaborators?.length }); - if(isOrganization(org)) { + if (isOrganization(org)) { await invalidate(Dependencies.ACCOUNT); await preloadData(`${base}/console/organization-${org.$id}`); await goto(`${base}/console/organization-${org.$id}`); From b568e8553c5eae5a308073a78815015e6e329820 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Tue, 3 Sep 2024 09:19:07 +0545 Subject: [PATCH 06/39] improve --- src/routes/console/create-organization/+page.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/console/create-organization/+page.svelte b/src/routes/console/create-organization/+page.svelte index 323d48c610..8dc928f004 100644 --- a/src/routes/console/create-organization/+page.svelte +++ b/src/routes/console/create-organization/+page.svelte @@ -90,7 +90,7 @@ } if ($page.url.searchParams.has('type')) { const type = $page.url.searchParams.get('type'); - if (type === 'confirmed') { + if (type === 'payment_confirmed') { const organizationId = $page.url.searchParams.get('id'); const invites = $page.url.searchParams.getAll('invites'); await validate(organizationId, invites); @@ -155,7 +155,7 @@ if (!isOrganization(org) && org.status == 402) { let clientSecret = org.clientSecret; let params = new URLSearchParams(); - params.append('type', 'confirmed'); + params.append('type', 'payment_confirmed'); params.append('id', org.teamId); for (let index = 0; index < collaborators.length; index++) { const invite = collaborators[index]; From 81d4ad8a6e2a5939edd15ffd5298f20e0ecdcbe1 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 11 Nov 2024 08:41:25 +0000 Subject: [PATCH 07/39] remove unused --- .../organization-[organization]/billing/paymentHistory.svelte | 1 - 1 file changed, 1 deletion(-) diff --git a/src/routes/(console)/organization-[organization]/billing/paymentHistory.svelte b/src/routes/(console)/organization-[organization]/billing/paymentHistory.svelte index 290b76b75b..e74739f537 100644 --- a/src/routes/(console)/organization-[organization]/billing/paymentHistory.svelte +++ b/src/routes/(console)/organization-[organization]/billing/paymentHistory.svelte @@ -28,7 +28,6 @@ import { onMount } from 'svelte'; import { trackEvent } from '$lib/actions/analytics'; import { selectedInvoice, showRetryModal } from './store'; - import { organization } from '$lib/stores/organization'; let showDropdown = []; let showFailedError = false; From c7a9e26de0ee343bc66e835b58533d5c7ec23ff9 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 11 Nov 2024 09:05:36 +0000 Subject: [PATCH 08/39] allow scale selection --- .../components/billing/planSelection.svelte | 50 +++++++++---------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/src/lib/components/billing/planSelection.svelte b/src/lib/components/billing/planSelection.svelte index 0d484a9b40..cfcb28bbab 100644 --- a/src/lib/components/billing/planSelection.svelte +++ b/src/lib/components/billing/planSelection.svelte @@ -76,31 +76,29 @@ - {#if $organization?.billingPlan === BillingPlan.SCALE} -
  • - - -
    -

    - {tierScale.name} - {#if $organization?.billingPlan === BillingPlan.SCALE && !isNewOrg} - Current plan - {/if} -

    -

    - {tierScale.description} -

    -

    - {formatCurrency(scalePlan?.price ?? 0)} per month + usage -

    -
    -
    -
    -
  • - {/if} +
  • + + +
    +

    + {tierScale.name} + {#if $organization?.billingPlan === BillingPlan.SCALE && !isNewOrg} + Current plan + {/if} +

    +

    + {tierScale.description} +

    +

    + {formatCurrency(scalePlan?.price ?? 0)} per month + usage +

    +
    +
    +
    +
  • {/if} From f29ca9a8e9776e643aad723c268bb549d1a63f34 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Thu, 14 Nov 2024 12:59:39 +0545 Subject: [PATCH 09/39] update plan upgrade --- src/lib/sdk/billing.ts | 14 ++- .../change-plan/+page.svelte | 116 +++++++++++------- 2 files changed, 86 insertions(+), 44 deletions(-) diff --git a/src/lib/sdk/billing.ts b/src/lib/sdk/billing.ts index f2428fd19d..bee94ffc3b 100644 --- a/src/lib/sdk/billing.ts +++ b/src/lib/sdk/billing.ts @@ -416,14 +416,22 @@ export class Billing { organizationId: string, billingPlan: string, paymentMethodId: string, - billingAddressId: string = undefined - ): Promise { + billingAddressId: string = undefined, + couponId: string = null, + invites: Array = [], + budget: number = undefined, + taxId: string = null + ): Promise { const path = `/organizations/${organizationId}/plan`; const params = { organizationId, billingPlan, paymentMethodId, - billingAddressId + billingAddressId, + couponId, + invites, + budget, + taxId }; const uri = new URL(this.client.config.endpoint + path); return await this.client.call( diff --git a/src/routes/(console)/organization-[organization]/change-plan/+page.svelte b/src/routes/(console)/organization-[organization]/change-plan/+page.svelte index 3682491c7f..573f1e9fb2 100644 --- a/src/routes/(console)/organization-[organization]/change-plan/+page.svelte +++ b/src/routes/(console)/organization-[organization]/change-plan/+page.svelte @@ -1,5 +1,5 @@
    - -

    {currentPlan.name} plan

    -

    {formatCurrency(currentPlan.price)}

    -
    - +
    +

    {selectedPlan.name} plan

    +

    {formatCurrency(selectedPlan.price)}

    +
    + +

    Additional seats ({collaborators?.length})

    - {formatCurrency(extraSeatsCost)} + {formatCurrency( + isScaleDowngrade + ? (collaborators?.length ?? 0) * (selectedPlan?.addons?.member?.price ?? 0) + : extraSeatsCost + )}

    - +
    + + {#if isScaleUpgrade} + {@const currentPlanName = currentOrgPlan.name} +
    +
    + Unused {currentPlanName} plan balance + + +
    +

    -{formatCurrency(unUsedBalances)}

    +
    + {:else if isScaleDowngrade} +
    + Unused {currentOrgPlan.name} plan balance +

    -{formatCurrency(currentOrgPlan.price)}

    +
    + {/if} + {#if couponData?.status === 'active'} {/if}
    - +

    Upcoming charge
    Due on {!currentPlan.trialDays + >Due on {!selectedPlan.trialDays ? toLocaleDate(billingPayDate.toString()) : toLocaleDate(trialEndDate.toString())}

    {formatCurrency(estimatedTotal)}

    - +

    - You'll pay {formatCurrency(estimatedTotal)} now, with our first - billing cycle starting on + You'll {isRefund ? 'receive a refund of' : 'pay'} + {formatCurrency(estimatedTotal)} + now, with our first billing cycle starting on {!currentPlan.trialDays + >{!selectedPlan.trialDays ? toLocaleDate(billingPayDate.toString()) : toLocaleDate(trialEndDate.toString())}. Once your credits run out, you'll be charged - {formatCurrency(currentPlan.price)} plus usage fees every 30 days. + {formatCurrency(selectedPlan.price)} plus usage fees every 30 days.

    diff --git a/src/routes/(console)/organization-[organization]/change-plan/+page.svelte b/src/routes/(console)/organization-[organization]/change-plan/+page.svelte index fe317b18a4..46c1e8b59e 100644 --- a/src/routes/(console)/organization-[organization]/change-plan/+page.svelte +++ b/src/routes/(console)/organization-[organization]/change-plan/+page.svelte @@ -23,7 +23,7 @@ InputTextarea, Label } from '$lib/elements/forms'; - import { formatCurrency } from '$lib/helpers/numbers.js'; + import { formatCurrency } from '$lib/helpers/numbers'; import { WizardSecondaryContainer, WizardSecondaryContent, @@ -39,7 +39,7 @@ type OrganizationError } from '$lib/stores/organization'; import { sdk } from '$lib/stores/sdk'; - import { confirmPayment } from '$lib/stores/stripe.js'; + import { confirmPayment } from '$lib/stores/stripe'; import { user } from '$lib/stores/user'; import { VARS } from '$lib/system'; import { onMount } from 'svelte'; @@ -107,8 +107,8 @@ billingPlan = BillingPlan.PRO; } - const currentPlan = await sdk.forConsole.billing.getPlan($organization?.$id); - selfService = currentPlan.selfService; + const currentOrgPlan = await sdk.forConsole.billing.getPlan($organization?.$id); + selfService = currentOrgPlan.selfService; }); async function loadPaymentMethods() { @@ -210,7 +210,7 @@ async function upgrade() { try { //Add collaborators - var newCollaborators = []; + let newCollaborators = []; if (collaborators?.length) { newCollaborators = collaborators.filter( (collaborator) => @@ -369,11 +369,12 @@ {#if billingPlan !== BillingPlan.FREE && $organization.billingPlan !== billingPlan && $organization.billingPlan !== BillingPlan.CUSTOM} + currentTier={$organization.billingPlan} /> {:else if $organization.billingPlan !== BillingPlan.CUSTOM} {/if} From bcbf5c75b5faa7ee1b82b3c0192949d586774809 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 19 Dec 2024 13:25:27 +0530 Subject: [PATCH 13/39] address comments. --- .../billing/estimatedTotalBox.svelte | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/src/lib/components/billing/estimatedTotalBox.svelte b/src/lib/components/billing/estimatedTotalBox.svelte index df06ef0b73..b73c87378c 100644 --- a/src/lib/components/billing/estimatedTotalBox.svelte +++ b/src/lib/components/billing/estimatedTotalBox.svelte @@ -34,17 +34,14 @@ : 0; $: extraSeatsCost = (collaborators?.length ?? 0) * (selectedPlan?.addons?.member?.price ?? 0); - $: grossCost = - isScaleUpgrade || isScaleDowngrade - ? selectedPlan.price + extraSeatsCost - unUsedBalances - : selectedPlan.price + extraSeatsCost; + $: grossCost = isScaleUpgrade + ? selectedPlan.price + extraSeatsCost - unUsedBalances + : selectedPlan.price + extraSeatsCost; $: estimatedTotal = couponData?.status === 'active' ? Math.max(0, grossCost - couponData.credits) : grossCost; $: trialEndDate = new Date( billingPayDate.getTime() + selectedPlan.trialDays * 24 * 60 * 60 * 1000 ); - - $: isRefund = isScaleDowngrade && grossCost < 0;

    -{formatCurrency(unUsedBalances)}

    - {:else if isScaleDowngrade} -
    - Unused {currentOrgPlan.name} plan balance -

    -{formatCurrency(currentOrgPlan.price)}

    -
    {/if} {#if couponData?.status === 'active'} @@ -107,15 +99,28 @@

    - You'll {isRefund ? 'receive a refund of' : 'pay'} - {formatCurrency(estimatedTotal)} - now, with our first billing cycle starting on - {!selectedPlan.trialDays - ? toLocaleDate(billingPayDate.toString()) - : toLocaleDate(trialEndDate.toString())}. Once your credits run out, you'll be charged - {formatCurrency(selectedPlan.price)} plus usage fees every 30 days. + {#if isScaleDowngrade} + You'll continue using the {currentOrgPlan.name} plan until your current cycle ends on + {!selectedPlan.trialDays + ? toLocaleDate(billingPayDate.toString()) + : toLocaleDate(trialEndDate.toString())}. Starting the next cycle, your plan will switch to {selectedPlan.name}, and you'll be + charged + {formatCurrency(grossCost)} + every 30 days. + {:else} + You'll pay + {formatCurrency(estimatedTotal)} + now, with our first billing cycle starting on + {!selectedPlan.trialDays + ? toLocaleDate(billingPayDate.toString()) + : toLocaleDate(trialEndDate.toString())}. Once your credits run out, you'll be charged + {formatCurrency(selectedPlan.price)} plus usage fees every 30 + days. + {/if}

    From b6a517a8cf952c9d3ba5d982a34e7c4f23935dff Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 19 Dec 2024 13:25:35 +0530 Subject: [PATCH 14/39] ran: formatter. --- src/lib/components/billing/planSelection.svelte | 6 +----- src/lib/sdk/billing.ts | 7 ++----- src/lib/stores/stripe.ts | 4 ++-- src/routes/(console)/account/organizations/+page.ts | 10 ++++------ src/routes/+layout.ts | 4 +++- 5 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/lib/components/billing/planSelection.svelte b/src/lib/components/billing/planSelection.svelte index cfcb28bbab..4a6e1f7caf 100644 --- a/src/lib/components/billing/planSelection.svelte +++ b/src/lib/components/billing/planSelection.svelte @@ -77,11 +77,7 @@
  • - +

    diff --git a/src/lib/sdk/billing.ts b/src/lib/sdk/billing.ts index bee94ffc3b..57ad227be1 100644 --- a/src/lib/sdk/billing.ts +++ b/src/lib/sdk/billing.ts @@ -318,14 +318,11 @@ export class Billing { ); } - async validateOrganization( - organizationId: string, - invites: string[], - ): Promise { + async validateOrganization(organizationId: string, invites: string[]): Promise { const path = `/organizations/${organizationId}/validate`; const params = { organizationId, - invites, + invites }; const uri = new URL(this.client.config.endpoint + path); return await this.client.call( diff --git a/src/lib/stores/stripe.ts b/src/lib/stores/stripe.ts index 3579746463..e62110d209 100644 --- a/src/lib/stores/stripe.ts +++ b/src/lib/stores/stripe.ts @@ -130,8 +130,8 @@ export async function confirmPayment( clientSecret: clientSecret, confirmParams: { return_url: url, - payment_method: paymentMethod.providerMethodId, - }, + payment_method: paymentMethod.providerMethodId + } }); if (error) { if (returnError) { diff --git a/src/routes/(console)/account/organizations/+page.ts b/src/routes/(console)/account/organizations/+page.ts index 803497c6a8..237a3772d7 100644 --- a/src/routes/(console)/account/organizations/+page.ts +++ b/src/routes/(console)/account/organizations/+page.ts @@ -10,13 +10,11 @@ export const load: PageLoad = async ({ url, route }) => { const limit = getLimit(url, route, CARD_LIMIT); const offset = pageToOffset(page, limit); - const queries = [ - Query.offset(offset), - Query.limit(limit), - Query.orderDesc('') - ]; + const queries = [Query.offset(offset), Query.limit(limit), Query.orderDesc('')]; - const organizations = isCloud ? await sdk.forConsole.billing.listOrganization(queries) : sdk.forConsole.teams.list(queries); + const organizations = isCloud + ? await sdk.forConsole.billing.listOrganization(queries) + : sdk.forConsole.teams.list(queries); return { offset, diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts index 6468cab571..4c9b760ca7 100644 --- a/src/routes/+layout.ts +++ b/src/routes/+layout.ts @@ -29,7 +29,9 @@ export const load: LayoutLoad = async ({ depends, url, route }) => { if (account) { return { account, - organizations: isCloud ? await sdk.forConsole.billing.listOrganization() : await sdk.forConsole.teams.list() + organizations: isCloud + ? await sdk.forConsole.billing.listOrganization() + : await sdk.forConsole.teams.list() }; } From 867320911c202c46e618cc904818a092b0a3c141 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 22 Dec 2024 13:14:06 +0545 Subject: [PATCH 15/39] update models --- src/lib/sdk/billing.ts | 24 ++++++++++++++++++------ src/lib/stores/organization.ts | 2 ++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/lib/sdk/billing.ts b/src/lib/sdk/billing.ts index bee94ffc3b..01e2008a5f 100644 --- a/src/lib/sdk/billing.ts +++ b/src/lib/sdk/billing.ts @@ -174,6 +174,10 @@ export type Aggregation = { * Usage logs for the billing period. */ resources: OrganizationUsage; + /** + * Aggregation billing plan + */ + plan: string; }; export type OrganizationUsage = { @@ -388,19 +392,27 @@ export class Billing { ); } - async getPlan(organizationId: string): Promise { + async getOrganizationPlan(organizationId: string): Promise { const path = `/organizations/${organizationId}/plan`; - const params = { - organizationId - }; const uri = new URL(this.client.config.endpoint + path); return await this.client.call( 'get', uri, { 'content-type': 'application/json' - }, - params + } + ); + } + + async getPlan(planId: string): Promise { + const path = `/console/plans/${planId}`; + const uri = new URL(this.client.config.endpoint + path); + return await this.client.call( + 'get', + uri, + { + 'content-type': 'application/json' + } ); } diff --git a/src/lib/stores/organization.ts b/src/lib/stores/organization.ts index 0e1912c5fc..53420f4e50 100644 --- a/src/lib/stores/organization.ts +++ b/src/lib/stores/organization.ts @@ -29,6 +29,8 @@ export type Organization = Models.Team> & { amount: number; billingTaxId?: string; billingPlanDowngrade?: Tier; + billingAggregationId: string; + billingInvoiceId: string; }; export type OrganizationList = { From 8fdf7c515f5f8470f17723cc7922c5adabf05702 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 22 Dec 2024 13:15:42 +0545 Subject: [PATCH 16/39] refactor and get current plan from aggregation --- .../billing/+page.svelte | 3 ++- .../billing/+page.ts | 25 ++++++++++++++----- .../billing/planSummary.svelte | 5 ++-- .../change-plan/+page.svelte | 2 +- .../usage/[[invoice]]/+page.ts | 2 +- .../storage/bucket-[bucket]/settings/+page.ts | 2 +- 6 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/routes/(console)/organization-[organization]/billing/+page.svelte b/src/routes/(console)/organization-[organization]/billing/+page.svelte index d3d6b7fd1a..ad680d21a2 100644 --- a/src/routes/(console)/organization-[organization]/billing/+page.svelte +++ b/src/routes/(console)/organization-[organization]/billing/+page.svelte @@ -123,7 +123,8 @@ diff --git a/src/routes/(console)/organization-[organization]/billing/+page.ts b/src/routes/(console)/organization-[organization]/billing/+page.ts index 0e945f906a..14957a4d3a 100644 --- a/src/routes/(console)/organization-[organization]/billing/+page.ts +++ b/src/routes/(console)/organization-[organization]/billing/+page.ts @@ -21,10 +21,16 @@ export const load: PageLoad = async ({ parent, depends }) => { const billingAddressId = (organization as Organization)?.billingAddressId; const billingAddressPromise: Promise
    = billingAddressId ? sdk.forConsole.billing - .getOrganizationBillingAddress(organization.$id, billingAddressId) - .catch(() => null) + .getOrganizationBillingAddress(organization.$id, billingAddressId) + .catch(() => null) : null; + const billingAggregationId = (organization as Organization)?.billingAggregationId; + const billingInvoiceId = (organization as Organization)?.billingInvoiceId; + + const billingAggregation = await sdk.forConsole.billing.getAggregation(organization.$id, billingAggregationId); + console.log(billingAggregation); + const [ paymentMethods, addressList, @@ -32,18 +38,22 @@ export const load: PageLoad = async ({ parent, depends }) => { billingAddress, currentPlan, creditList, - invoices + invoices, + aggregationBillingPlan, + billingInvoice, ] = await Promise.all([ sdk.forConsole.billing.listPaymentMethods(), sdk.forConsole.billing.listAddresses(), sdk.forConsole.billing.listAggregation(organization.$id), billingAddressPromise, - sdk.forConsole.billing.getPlan(organization.$id), + sdk.forConsole.billing.getOrganizationPlan(organization.$id), sdk.forConsole.billing.listCredits(organization.$id), sdk.forConsole.billing.listInvoices(organization.$id, [ Query.limit(1), Query.equal('from', organization.billingCurrentInvoiceDate) - ]) + ]), + sdk.forConsole.billing.getPlan(billingAggregation.plan), + sdk.forConsole.billing.getInvoice(organization.$id, billingInvoiceId) ]); return { @@ -52,7 +62,10 @@ export const load: PageLoad = async ({ parent, depends }) => { aggregationList, billingAddress, currentPlan, + aggregationBillingPlan, creditList, - invoices + invoices, + billingAggregation, + billingInvoice }; }; diff --git a/src/routes/(console)/organization-[organization]/billing/planSummary.svelte b/src/routes/(console)/organization-[organization]/billing/planSummary.svelte index c84356b753..f1c6c64650 100644 --- a/src/routes/(console)/organization-[organization]/billing/planSummary.svelte +++ b/src/routes/(console)/organization-[organization]/billing/planSummary.svelte @@ -17,8 +17,9 @@ export let members: Models.MembershipList; export let currentPlan: Plan; export let creditList: CreditList; + export let currentInvoice: Invoice | undefined = undefined - const currentInvoice: Invoice | undefined = invoices.length > 0 ? invoices[0] : undefined; + // const currentInvoice: Invoice | undefined = invoices.length > 0 ? invoices[0] : undefined; const extraMembers = members.total > 1 ? members.total - 1 : 0; const availableCredit = creditList.available; const today = new Date(); @@ -45,7 +46,7 @@ - {tierToPlan($organization?.billingPlan)?.name} plan + {currentPlan.name} plan
    {isTrial || $organization?.billingPlan === BillingPlan.GITHUB_EDUCATION ? formatCurrency(0) diff --git a/src/routes/(console)/organization-[organization]/change-plan/+page.svelte b/src/routes/(console)/organization-[organization]/change-plan/+page.svelte index fe317b18a4..a44ab8ff08 100644 --- a/src/routes/(console)/organization-[organization]/change-plan/+page.svelte +++ b/src/routes/(console)/organization-[organization]/change-plan/+page.svelte @@ -107,7 +107,7 @@ billingPlan = BillingPlan.PRO; } - const currentPlan = await sdk.forConsole.billing.getPlan($organization?.$id); + const currentPlan = await sdk.forConsole.billing.getOrganizationPlan($organization?.$id); selfService = currentPlan.selfService; }); diff --git a/src/routes/(console)/organization-[organization]/usage/[[invoice]]/+page.ts b/src/routes/(console)/organization-[organization]/usage/[[invoice]]/+page.ts index 9ffafbac21..b3546b01de 100644 --- a/src/routes/(console)/organization-[organization]/usage/[[invoice]]/+page.ts +++ b/src/routes/(console)/organization-[organization]/usage/[[invoice]]/+page.ts @@ -45,7 +45,7 @@ export const load: PageLoad = async ({ params, parent }) => { sdk.forConsole.billing.listInvoices(org.$id, [Query.orderDesc('from')]), sdk.forConsole.billing.listUsage(params.organization, startDate, endDate), sdk.forConsole.teams.listMemberships(params.organization), - sdk.forConsole.billing.getPlan(org.$id) + sdk.forConsole.billing.getOrganizationPlan(org.$id) ]); const projectNames: { [key: string]: Models.Project } = {}; diff --git a/src/routes/(console)/project-[project]/storage/bucket-[bucket]/settings/+page.ts b/src/routes/(console)/project-[project]/storage/bucket-[bucket]/settings/+page.ts index 546bfa7a8a..83972d5a6a 100644 --- a/src/routes/(console)/project-[project]/storage/bucket-[bucket]/settings/+page.ts +++ b/src/routes/(console)/project-[project]/storage/bucket-[bucket]/settings/+page.ts @@ -3,7 +3,7 @@ import { sdk } from '$lib/stores/sdk'; export const load: PageLoad = async ({ parent }) => { const { organization } = await parent(); - const currentPlan = await sdk.forConsole.billing.getPlan(organization.$id); + const currentPlan = await sdk.forConsole.billing.getOrganizationPlan(organization.$id); return { currentPlan }; From 1ee29b20777877ed6472b1aded55122168e716f8 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 22 Dec 2024 13:55:58 +0545 Subject: [PATCH 17/39] update summary --- .../organization-[organization]/billing/planSummary.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/(console)/organization-[organization]/billing/planSummary.svelte b/src/routes/(console)/organization-[organization]/billing/planSummary.svelte index f1c6c64650..2ef69316b7 100644 --- a/src/routes/(console)/organization-[organization]/billing/planSummary.svelte +++ b/src/routes/(console)/organization-[organization]/billing/planSummary.svelte @@ -39,7 +39,7 @@

    - Billing period: {toLocaleDate($organization?.billingCurrentInvoiceDate)} - {toLocaleDate( + Due at: {toLocaleDate( $organization?.billingNextInvoiceDate )}

    From 16705d38aac193517c7de52b730acd12e17f040a Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 23 Dec 2024 07:11:16 +0545 Subject: [PATCH 18/39] fix rename --- src/routes/(console)/organization-[organization]/+layout.ts | 2 +- src/routes/(console)/project-[project]/+layout.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/(console)/organization-[organization]/+layout.ts b/src/routes/(console)/organization-[organization]/+layout.ts index 650990e866..cd1968ea8e 100644 --- a/src/routes/(console)/organization-[organization]/+layout.ts +++ b/src/routes/(console)/organization-[organization]/+layout.ts @@ -27,7 +27,7 @@ export const load: LayoutLoad = async ({ params, depends }) => { const res = await sdk.forConsole.billing.getRoles(params.organization); roles = res.roles; scopes = res.scopes; - currentPlan = await sdk.forConsole.billing.getPlan(params.organization); + currentPlan = await sdk.forConsole.billing.getOrganizationPlan(params.organization); if (scopes.includes('billing.read')) { await failedInvoice.load(params.organization); if (get(failedInvoice)) { diff --git a/src/routes/(console)/project-[project]/+layout.ts b/src/routes/(console)/project-[project]/+layout.ts index 018462d75a..8725851436 100644 --- a/src/routes/(console)/project-[project]/+layout.ts +++ b/src/routes/(console)/project-[project]/+layout.ts @@ -29,7 +29,7 @@ export const load: LayoutLoad = async ({ params, depends }) => { let roles = isCloud ? [] : defaultRoles; let scopes = isCloud ? [] : defaultScopes; if (isCloud) { - currentPlan = await sdk.forConsole.billing.getPlan(project.teamId); + currentPlan = await sdk.forConsole.billing.getOrganizationPlan(project.teamId); const res = await sdk.forConsole.billing.getRoles(project.teamId); roles = res.roles; scopes = res.scopes; From 14a0cacbf68c0e41ec36ef82e32c2ef90489d370 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 23 Dec 2024 08:17:14 +0545 Subject: [PATCH 19/39] refactor --- .../organization-[organization]/billing/+page.svelte | 3 +-- .../(console)/organization-[organization]/billing/+page.ts | 6 ------ .../organization-[organization]/billing/planSummary.svelte | 3 +-- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/routes/(console)/organization-[organization]/billing/+page.svelte b/src/routes/(console)/organization-[organization]/billing/+page.svelte index ad680d21a2..bf09c55759 100644 --- a/src/routes/(console)/organization-[organization]/billing/+page.svelte +++ b/src/routes/(console)/organization-[organization]/billing/+page.svelte @@ -124,8 +124,7 @@ creditList={data?.creditList} members={data?.members} currentPlan={data?.aggregationBillingPlan} - currentInvoice={data?.billingInvoice} - invoices={data?.invoices.invoices} /> + currentInvoice={data?.billingInvoice} /> diff --git a/src/routes/(console)/organization-[organization]/billing/+page.ts b/src/routes/(console)/organization-[organization]/billing/+page.ts index c98183028b..25c390c2a4 100644 --- a/src/routes/(console)/organization-[organization]/billing/+page.ts +++ b/src/routes/(console)/organization-[organization]/billing/+page.ts @@ -36,7 +36,6 @@ export const load: PageLoad = async ({ parent, depends }) => { aggregationList, billingAddress, creditList, - invoices, aggregationBillingPlan, billingInvoice, ] = await Promise.all([ @@ -45,10 +44,6 @@ export const load: PageLoad = async ({ parent, depends }) => { sdk.forConsole.billing.listAggregation(organization.$id), billingAddressPromise, sdk.forConsole.billing.listCredits(organization.$id), - sdk.forConsole.billing.listInvoices(organization.$id, [ - Query.limit(1), - Query.equal('from', organization.billingCurrentInvoiceDate) - ]), sdk.forConsole.billing.getPlan(billingAggregation.plan), sdk.forConsole.billing.getInvoice(organization.$id, billingInvoiceId) ]); @@ -60,7 +55,6 @@ export const load: PageLoad = async ({ parent, depends }) => { billingAddress, aggregationBillingPlan, creditList, - invoices, billingAggregation, billingInvoice }; diff --git a/src/routes/(console)/organization-[organization]/billing/planSummary.svelte b/src/routes/(console)/organization-[organization]/billing/planSummary.svelte index 2ef69316b7..eeaee9aba5 100644 --- a/src/routes/(console)/organization-[organization]/billing/planSummary.svelte +++ b/src/routes/(console)/organization-[organization]/billing/planSummary.svelte @@ -3,7 +3,7 @@ import { CardGrid, Collapsible, CollapsibleItem, Heading } from '$lib/components'; import { Button } from '$lib/elements/forms'; import { toLocaleDate } from '$lib/helpers/date'; - import { plansInfo, tierToPlan, upgradeURL } from '$lib/stores/billing'; + import { plansInfo, upgradeURL } from '$lib/stores/billing'; import { organization } from '$lib/stores/organization'; import type { CreditList, Invoice, Plan } from '$lib/sdk/billing'; import { abbreviateNumber, formatCurrency, formatNumberWithCommas } from '$lib/helpers/numbers'; @@ -13,7 +13,6 @@ import { tooltip } from '$lib/actions/tooltip'; import { type Models } from '@appwrite.io/console'; - export let invoices: Array; export let members: Models.MembershipList; export let currentPlan: Plan; export let creditList: CreditList; From 9f517bd0681a462a6d23c3dacd7011ac8e1be843 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 23 Dec 2024 08:38:16 +0545 Subject: [PATCH 20/39] improvements on error --- .../billing/+page.ts | 21 ++++++++++++------- .../billing/planSummary.svelte | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/routes/(console)/organization-[organization]/billing/+page.ts b/src/routes/(console)/organization-[organization]/billing/+page.ts index 25c390c2a4..12db3b5479 100644 --- a/src/routes/(console)/organization-[organization]/billing/+page.ts +++ b/src/routes/(console)/organization-[organization]/billing/+page.ts @@ -4,7 +4,6 @@ import type { Organization } from '$lib/stores/organization'; import { sdk } from '$lib/stores/sdk'; import { redirect } from '@sveltejs/kit'; import type { PageLoad } from './$types'; -import { Query } from '@appwrite.io/console'; export const load: PageLoad = async ({ parent, depends }) => { const { organization, scopes } = await parent(); @@ -26,32 +25,40 @@ export const load: PageLoad = async ({ parent, depends }) => { : null; const billingAggregationId = (organization as Organization)?.billingAggregationId; + + let billingAggregation = null; + try { + billingAggregation = await sdk.forConsole.billing.getAggregation(organization.$id, billingAggregationId); + } catch (e) { + // ignore error + } + const billingInvoiceId = (organization as Organization)?.billingInvoiceId; + let billingInvoice = null; + try { + billingInvoice = await sdk.forConsole.billing.getInvoice(organization.$id, billingInvoiceId) + } catch (e) { + // ignore error + } - const billingAggregation = await sdk.forConsole.billing.getAggregation(organization.$id, billingAggregationId); const [ paymentMethods, addressList, - aggregationList, billingAddress, creditList, aggregationBillingPlan, - billingInvoice, ] = await Promise.all([ sdk.forConsole.billing.listPaymentMethods(), sdk.forConsole.billing.listAddresses(), - sdk.forConsole.billing.listAggregation(organization.$id), billingAddressPromise, sdk.forConsole.billing.listCredits(organization.$id), sdk.forConsole.billing.getPlan(billingAggregation.plan), - sdk.forConsole.billing.getInvoice(organization.$id, billingInvoiceId) ]); return { paymentMethods, addressList, - aggregationList, billingAddress, aggregationBillingPlan, creditList, diff --git a/src/routes/(console)/organization-[organization]/billing/planSummary.svelte b/src/routes/(console)/organization-[organization]/billing/planSummary.svelte index eeaee9aba5..d642ae1087 100644 --- a/src/routes/(console)/organization-[organization]/billing/planSummary.svelte +++ b/src/routes/(console)/organization-[organization]/billing/planSummary.svelte @@ -54,7 +54,7 @@ : ''}
    - {#if $organization?.billingPlan !== BillingPlan.FREE && $organization?.billingPlan !== BillingPlan.GITHUB_EDUCATION && extraUsage > 0} + {#if currentPlan.budgeting && extraUsage > 0} Add-ons Date: Tue, 24 Dec 2024 07:09:19 +0545 Subject: [PATCH 21/39] credit support from plan --- src/lib/sdk/billing.ts | 1 + .../organization-[organization]/billing/planSummary.svelte | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/sdk/billing.ts b/src/lib/sdk/billing.ts index ef581a0eb7..711be9befd 100644 --- a/src/lib/sdk/billing.ts +++ b/src/lib/sdk/billing.ts @@ -291,6 +291,7 @@ export type Plan = { backupsEnabled: boolean; backupPolicies: number; emailBranding: boolean; + supportsCredit: boolean; }; export type PlansInfo = { diff --git a/src/routes/(console)/organization-[organization]/billing/planSummary.svelte b/src/routes/(console)/organization-[organization]/billing/planSummary.svelte index 3d05ac3a61..d71c709d87 100644 --- a/src/routes/(console)/organization-[organization]/billing/planSummary.svelte +++ b/src/routes/(console)/organization-[organization]/billing/planSummary.svelte @@ -132,7 +132,7 @@ {/if} - {#if $organization?.billingPlan !== BillingPlan.FREE && availableCredit > 0} + {#if currentPlan.supportsCredit && availableCredit > 0}

    - Due at: {toLocaleDate( - $organization?.billingNextInvoiceDate - )} + Due at: {toLocaleDate($organization?.billingNextInvoiceDate)}

    - - {currentPlan.name} plan + {currentPlan.name} plan
    {isTrial || $organization?.billingPlan === BillingPlan.GITHUB_EDUCATION ? formatCurrency(0) @@ -204,17 +213,21 @@
    {:else}
    - + {#if $organization?.billingPlanDowngrade !== null} + + {:else} + + {/if} diff --git a/src/routes/(console)/organization-[organization]/change-plan/+page.svelte b/src/routes/(console)/organization-[organization]/change-plan/+page.svelte index 2158fdeb12..8e1a777c92 100644 --- a/src/routes/(console)/organization-[organization]/change-plan/+page.svelte +++ b/src/routes/(console)/organization-[organization]/change-plan/+page.svelte @@ -155,6 +155,8 @@ }) }); + await invalidate(Dependencies.ORGANIZATION); + await goto(previousPage); addNotification({ type: 'success', From fb926d9d6fb4e02b3506bce632ef6a5be7d12e7b Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Tue, 24 Dec 2024 08:02:47 +0545 Subject: [PATCH 23/39] cancel downgrade --- .../billing/cancelDowngradeModel.svelte | 51 +++++++++++++++++++ .../billing/planSummary.svelte | 21 +++----- .../settings/deleteOrganizationModal.svelte | 2 +- 3 files changed, 58 insertions(+), 16 deletions(-) create mode 100644 src/routes/(console)/organization-[organization]/billing/cancelDowngradeModel.svelte diff --git a/src/routes/(console)/organization-[organization]/billing/cancelDowngradeModel.svelte b/src/routes/(console)/organization-[organization]/billing/cancelDowngradeModel.svelte new file mode 100644 index 0000000000..78785305b1 --- /dev/null +++ b/src/routes/(console)/organization-[organization]/billing/cancelDowngradeModel.svelte @@ -0,0 +1,51 @@ + + +
    + +

    + Your organization is set to change to + {tierToPlan($organization?.billingPlanDowngrade).name} + plan on {toLocaleDate($organization.billingNextInvoiceDate)}. +

    +

    Are you sure you want to cancel the change?

    + + + + +
    +
    diff --git a/src/routes/(console)/organization-[organization]/billing/planSummary.svelte b/src/routes/(console)/organization-[organization]/billing/planSummary.svelte index d9b9ddc08d..09cc3754e5 100644 --- a/src/routes/(console)/organization-[organization]/billing/planSummary.svelte +++ b/src/routes/(console)/organization-[organization]/billing/planSummary.svelte @@ -8,18 +8,19 @@ import type { CreditList, Invoice, Plan } from '$lib/sdk/billing'; import { abbreviateNumber, formatCurrency, formatNumberWithCommas } from '$lib/helpers/numbers'; import { humanFileSize } from '$lib/helpers/sizeConvertion'; - import { BillingPlan, Dependencies } from '$lib/constants'; + import { BillingPlan } from '$lib/constants'; import { trackEvent } from '$lib/actions/analytics'; import { tooltip } from '$lib/actions/tooltip'; import { type Models } from '@appwrite.io/console'; - import { sdk } from '$lib/stores/sdk'; - import { invalidate } from '$app/navigation'; + import CancelDowngradeModel from './cancelDowngradeModel.svelte'; export let members: Models.MembershipList; export let currentPlan: Plan; export let creditList: CreditList; export let currentInvoice: Invoice | undefined = undefined; + let showCancel: boolean = false; + const extraMembers = members.total > 1 ? members.total - 1 : 0; const availableCredit = creditList.available; const today = new Date(); @@ -28,17 +29,6 @@ $plansInfo.get($organization.billingPlan)?.trialDays; const extraUsage = currentInvoice ? currentInvoice.amount - currentPlan?.price : 0; const extraAddons = currentInvoice ? currentInvoice.usage?.length : 0; - - const cancelDowngrade = async () => { - await sdk.forConsole.billing.cancelDowngrade( - $organization.$id - ); - await invalidate(Dependencies.ORGANIZATION); - trackEvent('click_organization_plan_update', { - from: 'button', - source: 'billing_tab' - }); - }; {#if $organization} @@ -214,7 +204,7 @@ {:else}
    {#if $organization?.billingPlanDowngrade !== null} - + {:else}
    diff --git a/src/routes/(console)/organization-[organization]/change-plan/+page.svelte b/src/routes/(console)/organization-[organization]/change-plan/+page.svelte index 8e1a777c92..e1072e2478 100644 --- a/src/routes/(console)/organization-[organization]/change-plan/+page.svelte +++ b/src/routes/(console)/organization-[organization]/change-plan/+page.svelte @@ -321,7 +321,7 @@ you will be charged {formatCurrency( extraMembers * - ($plansInfo?.get(billingPlan)?.addons?.member?.price ?? 0) + ($plansInfo?.get(billingPlan)?.usage?.member?.price ?? 0) )} monthly for {extraMembers} team members. This will be reflected in your next invoice. diff --git a/src/routes/(console)/organization-[organization]/usage/[[invoice]]/totalMembers.svelte b/src/routes/(console)/organization-[organization]/usage/[[invoice]]/totalMembers.svelte index d63585b52d..b92803eaef 100644 --- a/src/routes/(console)/organization-[organization]/usage/[[invoice]]/totalMembers.svelte +++ b/src/routes/(console)/organization-[organization]/usage/[[invoice]]/totalMembers.svelte @@ -46,7 +46,7 @@

    From 34f0727d5fc3689fb75c19dcf45113487c41fc35 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 9 Jan 2025 11:48:59 +0530 Subject: [PATCH 25/39] ci: empty commit From 5d84c3b52c8dfcb8640565dbde614fa1256483ae Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 12 Jan 2025 07:28:45 +0000 Subject: [PATCH 26/39] fix types --- .../components/billing/estimatedTotal.svelte | 16 ++++ src/lib/sdk/billing.ts | 80 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 src/lib/components/billing/estimatedTotal.svelte diff --git a/src/lib/components/billing/estimatedTotal.svelte b/src/lib/components/billing/estimatedTotal.svelte new file mode 100644 index 0000000000..40cac8a00a --- /dev/null +++ b/src/lib/components/billing/estimatedTotal.svelte @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/src/lib/sdk/billing.ts b/src/lib/sdk/billing.ts index 85673c6156..04cbbaef6f 100644 --- a/src/lib/sdk/billing.ts +++ b/src/lib/sdk/billing.ts @@ -65,6 +65,28 @@ export type InvoiceList = { total: number; }; +export type Estimation = { + amount: number; + grossAmount: number; + credits: number; + discount: number; + items: EstimationItem[]; +} + +export type EstimationItem = { + label: string; + value: number; +} + +export type EstimationDeleteOrganization = { + amount: number; + grossAmount: number; + credits: number; + discount: number; + items: EstimationItem[], + unpaidInvoices: Invoice[]; +} + export type Coupon = { $id: string; code: string; @@ -396,6 +418,28 @@ export class Billing { ); } + async estimationCreateOrganization( + billingPlan: string, + couponId: string = null, + invites: Array = [], + ): Promise { + const path = `/organizations`; + const params = { + billingPlan, + couponId, + invites, + }; + const uri = new URL(this.client.config.endpoint + path); + return await this.client.call( + 'POST', + uri, + { + 'content-type': 'application/json' + }, + params + ); + } + async deleteOrganization(organizationId: string): Promise { const path = `/organizations/${organizationId}`; const params = { @@ -412,6 +456,18 @@ export class Billing { ); } + async estimationDeleteOrganization(organizationId: string): Promise { + const path = `/organizations/${organizationId}/predictions/delete-organization`; + const uri = new URL(this.client.config.endpoint + path); + return await this.client.call( + 'patch', + uri, + { + 'content-type': 'application/json' + } + ); + } + async getOrganizationPlan(organizationId: string): Promise { const path = `/organizations/${organizationId}/plan`; const uri = new URL(this.client.config.endpoint + path); @@ -475,6 +531,30 @@ export class Billing { params ); } + + async estimationUpdatePlan( + organizationId: string, + billingPlan: string, + couponId: string = null, + invites: Array = [], + ): Promise { + const path = `/organizations/${organizationId}/predictions/update-plan`; + const params = { + billingPlan, + couponId, + invites, + }; + const uri = new URL(this.client.config.endpoint + path); + return await this.client.call( + 'patch', + uri, + { + 'content-type': 'application/json' + }, + params + ); + } + async cancelDowngrade( organizationId: string ): Promise { From 31b1467a98ab23500f30063582bf1a91744fcb76 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Tue, 14 Jan 2025 08:05:18 +0000 Subject: [PATCH 27/39] use new estimation api for update plan and create organiation --- .../billing/discountsApplied.svelte | 21 +++++ .../components/billing/estimatedTotal.svelte | 93 +++++++++++++++++-- src/lib/sdk/billing.ts | 11 ++- .../create-organization/+page.svelte | 12 +++ .../change-plan/+page.svelte | 16 +++- 5 files changed, 141 insertions(+), 12 deletions(-) create mode 100644 src/lib/components/billing/discountsApplied.svelte diff --git a/src/lib/components/billing/discountsApplied.svelte b/src/lib/components/billing/discountsApplied.svelte new file mode 100644 index 0000000000..0a815a88cc --- /dev/null +++ b/src/lib/components/billing/discountsApplied.svelte @@ -0,0 +1,21 @@ + + +{#if value > 0} + +
    +

    +

    +
    + -{formatCurrency(value)} +
    +{/if} diff --git a/src/lib/components/billing/estimatedTotal.svelte b/src/lib/components/billing/estimatedTotal.svelte index 40cac8a00a..f39f94f96e 100644 --- a/src/lib/components/billing/estimatedTotal.svelte +++ b/src/lib/components/billing/estimatedTotal.svelte @@ -1,16 +1,97 @@ + +{#if estimation} + +
    + + {#each estimation.items ?? [] as item} + +

    {item.label}

    +

    {formatCurrency(item.value)}

    +
    + {/each} + {#each estimation.discounts ?? [] as item} + + {/each} +
    + +

    + Total due
    +

    +

    + {formatCurrency(estimation.grossAmount)} +

    +
    + + - \ No newline at end of file + +
    +{/if} \ No newline at end of file diff --git a/src/lib/sdk/billing.ts b/src/lib/sdk/billing.ts index 7305e8abe1..f4734d500a 100644 --- a/src/lib/sdk/billing.ts +++ b/src/lib/sdk/billing.ts @@ -71,6 +71,9 @@ export type Estimation = { credits: number; discount: number; items: EstimationItem[]; + discounts: EstimationItem[]; + trialDays: number; + trialEndDate: string | undefined; } export type EstimationItem = { @@ -420,7 +423,7 @@ export class Billing { couponId: string = null, invites: Array = [], ): Promise { - const path = `/organizations`; + const path = `/organizations/estimations/create-organization`; const params = { billingPlan, couponId, @@ -428,7 +431,7 @@ export class Billing { }; const uri = new URL(this.client.config.endpoint + path); return await this.client.call( - 'POST', + 'patch', uri, { 'content-type': 'application/json' @@ -454,7 +457,7 @@ export class Billing { } async estimationDeleteOrganization(organizationId: string): Promise { - const path = `/organizations/${organizationId}/predictions/delete-organization`; + const path = `/organizations/${organizationId}/estimations/delete-organization`; const uri = new URL(this.client.config.endpoint + path); return await this.client.call( 'patch', @@ -535,7 +538,7 @@ export class Billing { couponId: string = null, invites: Array = [], ): Promise { - const path = `/organizations/${organizationId}/predictions/update-plan`; + const path = `/organizations/${organizationId}/estimations/update-plan`; const params = { billingPlan, couponId, diff --git a/src/routes/(console)/create-organization/+page.svelte b/src/routes/(console)/create-organization/+page.svelte index 05da83c37f..59e84076cf 100644 --- a/src/routes/(console)/create-organization/+page.svelte +++ b/src/routes/(console)/create-organization/+page.svelte @@ -9,6 +9,7 @@ PlanSelection, SelectPaymentMethod } from '$lib/components/billing'; + import EstimatedTotal from '$lib/components/billing/estimatedTotal.svelte'; import ValidateCreditModal from '$lib/components/billing/validateCreditModal.svelte'; import Default from '$lib/components/roles/default.svelte'; import { BillingPlan, Dependencies } from '$lib/constants'; @@ -51,6 +52,7 @@ let billingPlan: BillingPlan = BillingPlan.FREE; let paymentMethodId: string; let collaborators: string[] = []; + let couponId: string | undefined; let couponData: Partial = { code: null, status: null, @@ -233,6 +235,11 @@ id="members" /> + {#if !couponData?.code}

    - -{formatCurrency(value)} + {#if value >= 100} +

    Credits applied

    + {:else} + -{formatCurrency(value)} + {/if} {/if} diff --git a/src/routes/(console)/apply-credit/+page.svelte b/src/routes/(console)/apply-credit/+page.svelte index 884fd1763e..74d003861f 100644 --- a/src/routes/(console)/apply-credit/+page.svelte +++ b/src/routes/(console)/apply-credit/+page.svelte @@ -8,6 +8,7 @@ EstimatedTotalBox, SelectPaymentMethod } from '$lib/components/billing'; + import EstimatedTotal from '$lib/components/billing/estimatedTotal.svelte'; import { BillingPlan, Dependencies } from '$lib/constants'; import { Button, Form, FormList, InputSelect, InputTags, InputText } from '$lib/elements/forms'; import { toLocaleDate } from '$lib/helpers/date'; @@ -285,12 +286,11 @@
  • {:else if selectedOrgId}
    - + couponId={couponData.code}> {#if campaign?.template === 'review' && (campaign?.cta || campaign?.claimed || campaign?.unclaimed)}

    {campaign?.cta}

    @@ -303,7 +303,7 @@

    {/if} -
    +
    {/if} From 293eac04ff0a9bfa15228560d92f5d7e88c56edd Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 15 Jan 2025 06:50:01 +0000 Subject: [PATCH 29/39] refactor plan selection --- src/lib/components/billing/index.ts | 2 + src/lib/components/billing/selectPlan.svelte | 52 +++++++++++++++++++ src/lib/sdk/billing.ts | 6 ++- .../create-organization/+page.svelte | 8 +-- .../change-plan/+page.svelte | 22 +++----- 5 files changed, 69 insertions(+), 21 deletions(-) create mode 100644 src/lib/components/billing/selectPlan.svelte diff --git a/src/lib/components/billing/index.ts b/src/lib/components/billing/index.ts index 266d217d8a..0643e5922d 100644 --- a/src/lib/components/billing/index.ts +++ b/src/lib/components/billing/index.ts @@ -7,3 +7,5 @@ export { default as PlanComparisonBox } from './planComparisonBox.svelte'; export { default as EmptyCardCloud } from './emptyCardCloud.svelte'; export { default as CreditsApplied } from './creditsApplied.svelte'; export { default as PlanSelection } from './planSelection.svelte'; +export { default as EstimatedTotal } from './estimatedTotal.svelte'; +export { default as SelectPlan } from './selectPlan.svelte'; \ No newline at end of file diff --git a/src/lib/components/billing/selectPlan.svelte b/src/lib/components/billing/selectPlan.svelte new file mode 100644 index 0000000000..ecff50f99f --- /dev/null +++ b/src/lib/components/billing/selectPlan.svelte @@ -0,0 +1,52 @@ + + +{#if billingPlan} +
      + {#each $plansInfo.values() as plan} +
    • + + +
      +

      + {plan.name} + {#if $organization?.billingPlan === plan.$id && !isNewOrg} + Current plan + {/if} +

      +

      + {plan.desc} +

      +

      + {formatCurrency(plan?.price ?? 0)} +

      +
      +
      +
      +
    • + {/each} +
    +{/if} diff --git a/src/lib/sdk/billing.ts b/src/lib/sdk/billing.ts index f4734d500a..2245053c66 100644 --- a/src/lib/sdk/billing.ts +++ b/src/lib/sdk/billing.ts @@ -296,7 +296,9 @@ export type PlanAddon = { export type Plan = { $id: string; name: string; + desc: string; price: number; + order: number; bandwidth: number; storage: number; webhooks: number; @@ -332,7 +334,7 @@ export type Plan = { supportsCredit: boolean; }; -export type PlansInfo = { +export type PlanList = { plans: Plan[]; total: number; }; @@ -1262,7 +1264,7 @@ export class Billing { ); } - async getPlansInfo(): Promise { + async getPlansInfo(): Promise { const path = `/console/plans`; const params = {}; const uri = new URL(this.client.config.endpoint + path); diff --git a/src/routes/(console)/create-organization/+page.svelte b/src/routes/(console)/create-organization/+page.svelte index 59e84076cf..3b758b0d39 100644 --- a/src/routes/(console)/create-organization/+page.svelte +++ b/src/routes/(console)/create-organization/+page.svelte @@ -6,8 +6,10 @@ import { EstimatedTotalBox, PlanComparisonBox, - PlanSelection, - SelectPaymentMethod + SelectPaymentMethod, + + SelectPlan + } from '$lib/components/billing'; import EstimatedTotal from '$lib/components/billing/estimatedTotal.svelte'; import ValidateCreditModal from '$lib/components/billing/validateCreditModal.svelte'; @@ -222,7 +224,7 @@ For more details on our plans, visit our .

    - + {#if billingPlan !== BillingPlan.FREE} $organization.billingPlan; - $: isDowngrade = billingPlan < $organization.billingPlan; + $: isUpgrade = $plansInfo.get(billingPlan).order > $currentPlan.order; + $: isDowngrade = $plansInfo.get(billingPlan).order < $currentPlan.order; $: if (billingPlan !== BillingPlan.FREE) { loadPaymentMethods(); } @@ -300,9 +299,8 @@ >Your contract is not eligible for manual changes. Please reach out to schedule a call or setup a dialog. {/if} - - {#if billingPlan !== BillingPlan.FREE && $organization.billingPlan !== billingPlan && $organization.billingPlan !== BillingPlan.CUSTOM} + {#if billingPlan !== BillingPlan.FREE && $organization.billingPlan !== billingPlan && $organization.billingPlan !== BillingPlan.CUSTOM && isUpgrade} - + {couponId} /> {:else if $organization.billingPlan !== BillingPlan.CUSTOM} {/if} From c5676817c3cfc63e2f8f001576aed2fb32bb1715 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 15 Jan 2025 07:31:04 +0000 Subject: [PATCH 30/39] fix after sync --- src/lib/components/billing/estimatedTotalBox.svelte | 10 +++++----- src/lib/components/billing/selectPlan.svelte | 1 - src/routes/(console)/apply-credit/+page.svelte | 7 +------ src/routes/+layout.ts | 1 - 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/lib/components/billing/estimatedTotalBox.svelte b/src/lib/components/billing/estimatedTotalBox.svelte index 581e261b5f..4d96f87c4d 100644 --- a/src/lib/components/billing/estimatedTotalBox.svelte +++ b/src/lib/components/billing/estimatedTotalBox.svelte @@ -28,12 +28,12 @@ $: currentOrgPlan = $plansInfo.get(currentTier); $: unUsedBalances = isScaleUpgrade ? currentOrgPlan.price + - (collaborators?.length ?? 0) * (currentOrgPlan?.addons?.member?.price ?? 0) + (collaborators?.length ?? 0) * (currentOrgPlan?.addons?.seats?.price ?? 0) : isScaleDowngrade ? currentOrgPlan.price : 0; - $: extraSeatsCost = (collaborators?.length ?? 0) * (selectedPlan?.addons?.member?.price ?? 0); + $: extraSeatsCost = (collaborators?.length ?? 0) * (selectedPlan?.addons?.seats?.price ?? 0); $: grossCost = isScaleUpgrade ? selectedPlan.price + extraSeatsCost - unUsedBalances : selectedPlan.price + extraSeatsCost; @@ -59,7 +59,7 @@

    {formatCurrency( isScaleDowngrade - ? (collaborators?.length ?? 0) * (selectedPlan?.addons?.member?.price ?? 0) + ? (collaborators?.length ?? 0) * (selectedPlan?.addons?.seats?.price ?? 0) : extraSeatsCost )}

    @@ -102,11 +102,11 @@ You'll pay {formatCurrency(estimatedTotal)} now, with your first billing cycle starting on {!currentPlan.trialDays + >{!currentOrgPlan.trialDays ? toLocaleDate(billingPayDate.toString()) : toLocaleDate(trialEndDate.toString())}. Once your credits run out, you'll be charged - {formatCurrency(currentPlan.price)} plus usage fees every 30 days. + {formatCurrency(currentOrgPlan.price)} plus usage fees every 30 days.

    diff --git a/src/lib/components/billing/selectPlan.svelte b/src/lib/components/billing/selectPlan.svelte index ecff50f99f..4447c17e3f 100644 --- a/src/lib/components/billing/selectPlan.svelte +++ b/src/lib/components/billing/selectPlan.svelte @@ -8,7 +8,6 @@ export let billingPlan: string; export let anyOrgFree = false; export let isNewOrg = false; - export let selfService = true; let classes: string = ''; export { classes as class }; diff --git a/src/routes/(console)/apply-credit/+page.svelte b/src/routes/(console)/apply-credit/+page.svelte index 502ede7ade..a33884a41d 100644 --- a/src/routes/(console)/apply-credit/+page.svelte +++ b/src/routes/(console)/apply-credit/+page.svelte @@ -3,12 +3,7 @@ import { base } from '$app/paths'; import { page } from '$app/stores'; import { Submit, trackError, trackEvent } from '$lib/actions/analytics'; - import { - CreditsApplied, - EstimatedTotalBox, - SelectPaymentMethod - } from '$lib/components/billing'; - import EstimatedTotal from '$lib/components/billing/estimatedTotal.svelte'; + import { CreditsApplied, EstimatedTotal, SelectPaymentMethod } from '$lib/components/billing'; import { BillingPlan, Dependencies } from '$lib/constants'; import { Button, Form, FormList, InputSelect, InputTags, InputText } from '$lib/elements/forms'; import { toLocaleDate } from '$lib/helpers/date'; diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts index eb96a29c26..8161662735 100644 --- a/src/routes/+layout.ts +++ b/src/routes/+layout.ts @@ -6,7 +6,6 @@ import { redirect } from '@sveltejs/kit'; import { Dependencies } from '$lib/constants'; import type { LayoutLoad } from './$types'; import { redirectTo } from './store'; -import { isCloud } from '$lib/system'; import { base } from '$app/paths'; import type { Account } from '$lib/stores/user'; import type { AppwriteException } from '@appwrite.io/console'; From 8c9c413e4f0f4611c2730657adbdf490c7db8e09 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 15 Jan 2025 07:54:29 +0000 Subject: [PATCH 31/39] fix coupon --- .../create-organization/+page.svelte | 14 ---------- .../change-plan/+page.svelte | 27 +++++-------------- 2 files changed, 7 insertions(+), 34 deletions(-) diff --git a/src/routes/(console)/create-organization/+page.svelte b/src/routes/(console)/create-organization/+page.svelte index 50ba69eff1..03ffc63d79 100644 --- a/src/routes/(console)/create-organization/+page.svelte +++ b/src/routes/(console)/create-organization/+page.svelte @@ -247,15 +247,6 @@ placeholder="Enter coupon code" id="couponId" /> - {#if !couponData?.code} - - {/if} {/if} @@ -265,11 +256,6 @@ {collaborators} {couponId} /> - {:else} {/if} diff --git a/src/routes/(console)/organization-[organization]/change-plan/+page.svelte b/src/routes/(console)/organization-[organization]/change-plan/+page.svelte index 7fd39a911b..f300a7bd0f 100644 --- a/src/routes/(console)/organization-[organization]/change-plan/+page.svelte +++ b/src/routes/(console)/organization-[organization]/change-plan/+page.svelte @@ -4,10 +4,7 @@ import { page } from '$app/stores'; import { Submit, trackError, trackEvent } from '$lib/actions/analytics'; import { Alert } from '$lib/components'; - import { - PlanComparisonBox, - SelectPaymentMethod - } from '$lib/components/billing'; + import { PlanComparisonBox, SelectPaymentMethod } from '$lib/components/billing'; import EstimatedTotal from '$lib/components/billing/estimatedTotal.svelte'; import PlanExcess from '$lib/components/billing/planExcess.svelte'; import SelectPlan from '$lib/components/billing/selectPlan.svelte'; @@ -299,10 +296,7 @@ >Your contract is not eligible for manual changes. Please reach out to schedule a call or setup a dialog. {/if} - + {#if isDowngrade} {#if billingPlan === BillingPlan.FREE} @@ -338,20 +332,13 @@ validityMessage="Invalid email address" id="members" /> +
    - {#if !couponData?.code} - - {/if} {/if} - - - {#if isDowngrade} Date: Wed, 15 Jan 2025 08:59:36 +0000 Subject: [PATCH 32/39] fix history not showing --- .../organization-[organization]/billing/paymentHistory.svelte | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/routes/(console)/organization-[organization]/billing/paymentHistory.svelte b/src/routes/(console)/organization-[organization]/billing/paymentHistory.svelte index 8a50a4efc3..5ee85510ca 100644 --- a/src/routes/(console)/organization-[organization]/billing/paymentHistory.svelte +++ b/src/routes/(console)/organization-[organization]/billing/paymentHistory.svelte @@ -62,8 +62,7 @@ request(); } - -{#if $organization?.billingPlan === BillingPlan.FREE && invoiceList.total > 0} +{#if $organization?.billingPlan !== BillingPlan.FREE && invoiceList.total > 0} Payment history From dee3b530debaabfebae519b517eff22894d2a7bb Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 15 Jan 2025 09:33:57 +0000 Subject: [PATCH 33/39] fix summary --- src/lib/sdk/billing.ts | 4 +++- .../billing/+page.svelte | 1 + .../billing/planSummary.svelte | 18 ++++++++---------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/lib/sdk/billing.ts b/src/lib/sdk/billing.ts index d3078688c9..0d0824561d 100644 --- a/src/lib/sdk/billing.ts +++ b/src/lib/sdk/billing.ts @@ -171,10 +171,12 @@ export type Aggregation = { * Total amount of the invoice. */ amount: number; + additionalMembers: number; + /** * Price for additional members */ - additionalMembers: number; + additionalMemberAmount: number; /** * Total storage usage. */ diff --git a/src/routes/(console)/organization-[organization]/billing/+page.svelte b/src/routes/(console)/organization-[organization]/billing/+page.svelte index 25b35dc327..cc0fb72d17 100644 --- a/src/routes/(console)/organization-[organization]/billing/+page.svelte +++ b/src/routes/(console)/organization-[organization]/billing/+page.svelte @@ -123,6 +123,7 @@ creditList={data?.creditList} members={data?.members} currentPlan={data?.aggregationBillingPlan} + currentAggregation={data?.billingAggregation} currentInvoice={data?.billingInvoice} /> diff --git a/src/routes/(console)/organization-[organization]/billing/planSummary.svelte b/src/routes/(console)/organization-[organization]/billing/planSummary.svelte index a2b3f05ced..3d8a89c6d4 100644 --- a/src/routes/(console)/organization-[organization]/billing/planSummary.svelte +++ b/src/routes/(console)/organization-[organization]/billing/planSummary.svelte @@ -5,7 +5,7 @@ import { toLocaleDate } from '$lib/helpers/date'; import { plansInfo, tierToPlan, upgradeURL } from '$lib/stores/billing'; import { organization } from '$lib/stores/organization'; - import type { CreditList, Invoice, Plan } from '$lib/sdk/billing'; + import type { Aggregation, CreditList, Invoice, Plan } from '$lib/sdk/billing'; import { abbreviateNumber, formatCurrency, formatNumberWithCommas } from '$lib/helpers/numbers'; import { humanFileSize } from '$lib/helpers/sizeConvertion'; import { BillingPlan } from '$lib/constants'; @@ -18,17 +18,16 @@ export let currentPlan: Plan; export let creditList: CreditList; export let currentInvoice: Invoice | undefined = undefined; + export let currentAggregation: Aggregation | undefined = undefined let showCancel: boolean = false; - const extraMembers = members.total > 1 ? members.total - 1 : 0; const availableCredit = creditList.available; const today = new Date(); const isTrial = new Date($organization?.billingStartDate).getTime() - today.getTime() > 0 && $plansInfo.get($organization.billingPlan)?.trialDays; const extraUsage = currentInvoice ? currentInvoice.amount - currentPlan?.price : 0; - const extraAddons = currentInvoice ? currentInvoice.usage?.length : 0; {#if $organization} @@ -64,7 +63,7 @@ Add-ons{extraMembers ? extraAddons + 1 : extraAddons} + >{currentAggregation.additionalMembers > 0 ? currentInvoice.usage.length + 1 : currentInvoice.usage.length}
    @@ -78,7 +77,7 @@
      - {#if extraMembers} + {#if currentAggregation.additionalMembers}
    • @@ -86,15 +85,14 @@
      {formatCurrency( - extraMembers * - (currentPlan?.addons?.seats?.price ?? 0) + currentAggregation.additionalMemberAmount )}
      - {extraMembers} + {currentAggregation.additionalMembers}
    • @@ -102,10 +100,10 @@ {#if currentInvoice?.usage} {#each currentInvoice.usage as excess, i}
    • 0 ? 'u-padding-block-8' : 'u-padding-block-start-8'}" - class:u-sep-block-start={i > 0 || extraMembers}> + class:u-sep-block-start={i > 0 || currentAggregation.additionalMembers > 0}> {#if ['storage', 'bandwidth'].includes(excess.name)} {@const excessValue = humanFileSize( excess.value From 654718d019cd4b41ae2c73e116e9b072cbd406f3 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 19 Jan 2025 07:31:42 +0000 Subject: [PATCH 34/39] fix errors --- .../components/billing/estimatedTotal.svelte | 23 +++++++++---------- .../(console)/apply-credit/+page.svelte | 1 + .../create-organization/+page.svelte | 2 +- .../change-plan/+page.svelte | 1 + 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/lib/components/billing/estimatedTotal.svelte b/src/lib/components/billing/estimatedTotal.svelte index f39f94f96e..fa7a3b34ea 100644 --- a/src/lib/components/billing/estimatedTotal.svelte +++ b/src/lib/components/billing/estimatedTotal.svelte @@ -1,4 +1,6 @@ -{#if estimation} +{#if isDowngrade} +
      + +

      + Your change will take effect once your current billing cycle ends on {toLocaleDate($organization.billingNextInvoiceDate)}. +

      +
      + +{:else if estimation}
      { if ($page.url.searchParams.has('coupon')) { - const coupon = $page.url.searchParams.get('coupon'); - try { - const response = await sdk.forConsole.billing.getCoupon(coupon); - couponData = response; - } catch (e) { - couponData = { - code: null, - status: null, - credits: null - }; - } + couponId = $page.url.searchParams.get('coupon'); } if ($page.url.searchParams.has('name')) { name = $page.url.searchParams.get('name'); @@ -152,7 +142,7 @@ billingPlan, paymentMethodId, null, - couponData?.code, + couponId, collaborators, billingBudget, taxId diff --git a/src/routes/(console)/organization-[organization]/change-plan/+page.svelte b/src/routes/(console)/organization-[organization]/change-plan/+page.svelte index 025d739863..35c358b604 100644 --- a/src/routes/(console)/organization-[organization]/change-plan/+page.svelte +++ b/src/routes/(console)/organization-[organization]/change-plan/+page.svelte @@ -360,12 +360,22 @@ {#if billingPlan !== BillingPlan.FREE && $organization.billingPlan !== billingPlan && $organization.billingPlan !== BillingPlan.CUSTOM && isUpgrade} {:else if $organization.billingPlan !== BillingPlan.CUSTOM} + {#if isDowngrade} + + {/if} {/if} From d64de9039de649249c402b370cfc2626045cffa8 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 20 Jan 2025 06:41:18 +0000 Subject: [PATCH 36/39] improve estimatie total --- .../components/billing/estimatedTotal.svelte | 162 +++++++++--------- .../change-plan/+page.svelte | 30 ++-- 2 files changed, 102 insertions(+), 90 deletions(-) diff --git a/src/lib/components/billing/estimatedTotal.svelte b/src/lib/components/billing/estimatedTotal.svelte index fef5436088..1af444fd36 100644 --- a/src/lib/components/billing/estimatedTotal.svelte +++ b/src/lib/components/billing/estimatedTotal.svelte @@ -1,21 +1,17 @@ -{#if isDowngrade} -
      - -

      - Your change will take effect once your current billing cycle ends on {toLocaleDate($organization.billingNextInvoiceDate)}. -

      -
      - +{#if error.length} + + + {error} + + {:else if estimation} - -
      - - {#each estimation.items ?? [] as item} +
      + + {#each estimation.items ?? [] as item} + +

      {item.label}

      +

      {formatCurrency(item.value)}

      +
      + {/each} + {#each estimation.discounts ?? [] as item} + + {/each} +
      -

      {item.label}

      -

      {formatCurrency(item.value)}

      +

      + Total due
      +

      +

      + {formatCurrency(estimation.grossAmount)} +

      - {/each} - {#each estimation.discounts ?? [] as item} - - {/each} -
      - -

      - Total due
      -

      -

      - {formatCurrency(estimation.grossAmount)} -

      -
      -

      - You'll pay {formatCurrency(estimation.amount)} now. Once your credits run out, you'll be charged - {formatCurrency(estimation.amount)} every 30 days. -

      +

      + You'll pay {formatCurrency(estimation.amount)} now. Once + your credits run out, you'll be charged + {formatCurrency(estimation.amount)} every 30 days. +

      - - - {#if budgetEnabled} -
      - -
      - {/if} -
      -
      -
      -{/if} \ No newline at end of file + + + {#if budgetEnabled} +
      + +
      + {/if} +
      +
      +
      +{/if} diff --git a/src/routes/(console)/organization-[organization]/change-plan/+page.svelte b/src/routes/(console)/organization-[organization]/change-plan/+page.svelte index 35c358b604..baf1f44ccb 100644 --- a/src/routes/(console)/organization-[organization]/change-plan/+page.svelte +++ b/src/routes/(console)/organization-[organization]/change-plan/+page.svelte @@ -21,6 +21,7 @@ InputTextarea, Label } from '$lib/elements/forms'; + import { toLocaleDate } from '$lib/helpers/date.js'; import { formatCurrency } from '$lib/helpers/numbers'; import { WizardSecondaryContainer, @@ -76,6 +77,7 @@ let taxId: string; let billingBudget: number; let showCreditModal = false; + let error: string = ''; let feedbackDowngradeReason: string; let feedbackMessage: string; @@ -83,6 +85,7 @@ onMount(async () => { if ($page.url.searchParams.has('code')) { + couponId = $page.url.searchParams.get('code'); const coupon = $page.url.searchParams.get('code'); try { const response = await sdk.forConsole.billing.getCoupon(coupon); @@ -275,7 +278,7 @@ $: if (billingPlan !== BillingPlan.FREE) { loadPaymentMethods(); } - $: isButtonDisabled = $organization.billingPlan === billingPlan; + $: isButtonDisabled = $organization.billingPlan === billingPlan || error.length > 0; @@ -360,21 +363,26 @@ {#if billingPlan !== BillingPlan.FREE && $organization.billingPlan !== billingPlan && $organization.billingPlan !== BillingPlan.CUSTOM && isUpgrade} {:else if $organization.billingPlan !== BillingPlan.CUSTOM} {#if isDowngrade} - +
      + +

      + Your change will take effect once your current billing cycle ends on {toLocaleDate($organization.billingNextInvoiceDate)}. +

      +
      {/if} {/if} @@ -386,7 +394,7 @@ From e696d0c05b667ef1478ae01819f31fffe6391315 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 20 Jan 2025 06:41:36 +0000 Subject: [PATCH 37/39] check estimation before delete flow --- .../settings/+page.svelte | 2 + .../deleteOrganizationEstimation.svelte | 31 +++ .../settings/deleteOrganizationModal.svelte | 203 +++++++++--------- .../settings/invoicesTable.svelte | 137 ++++++++++++ 4 files changed, 276 insertions(+), 97 deletions(-) create mode 100644 src/routes/(console)/organization-[organization]/settings/deleteOrganizationEstimation.svelte create mode 100644 src/routes/(console)/organization-[organization]/settings/invoicesTable.svelte diff --git a/src/routes/(console)/organization-[organization]/settings/+page.svelte b/src/routes/(console)/organization-[organization]/settings/+page.svelte index 98b236e6d7..361a52d73e 100644 --- a/src/routes/(console)/organization-[organization]/settings/+page.svelte +++ b/src/routes/(console)/organization-[organization]/settings/+page.svelte @@ -15,10 +15,12 @@ import { isCloud } from '$lib/system'; import Baa from './BAA.svelte'; import Soc2 from './Soc2.svelte'; + import DeleteOrganizationEstimation from './deleteOrganizationEstimation.svelte'; export let data; let name: string; let showDelete = false; + let showDeleteEstimation = false; onMount(() => { name = $organization.name; diff --git a/src/routes/(console)/organization-[organization]/settings/deleteOrganizationEstimation.svelte b/src/routes/(console)/organization-[organization]/settings/deleteOrganizationEstimation.svelte new file mode 100644 index 0000000000..3d6a967b9d --- /dev/null +++ b/src/routes/(console)/organization-[organization]/settings/deleteOrganizationEstimation.svelte @@ -0,0 +1,31 @@ + + +{#if error} + + {error} + +{/if} +{#if estimation} + {#if estimation.unpaidInvoices?.length > 0} + + + You have a unpaid invoices. Please settle them first in order to delete team. + + + + + {/if} +{/if} + +{#if $selectedInvoice} + +{/if} diff --git a/src/routes/(console)/organization-[organization]/settings/deleteOrganizationModal.svelte b/src/routes/(console)/organization-[organization]/settings/deleteOrganizationModal.svelte index 78c9e29463..26a3384917 100644 --- a/src/routes/(console)/organization-[organization]/settings/deleteOrganizationModal.svelte +++ b/src/routes/(console)/organization-[organization]/settings/deleteOrganizationModal.svelte @@ -1,5 +1,5 @@
      @@ -108,102 +124,95 @@ icon="exclamation" state="warning" headerDivider={false}> - {#if upcomingInvoice} - - - You have a pending {formatCurrency(upcomingInvoice.grossAmount)} invoice for your - {tierToPlan(upcomingInvoice.plan).name} plan - -

      - By proceeding, your invoice will be processed within the hour. Upon successful - payment, your organization will be deleted. -

      -
      - {/if} + {#if estimation} + + {:else} +

      + {#if $projects.total > 0} + The following projects and all data associated with {$organization.name} + will be permanently deleted. This action is irreversible. + {:else} + All data associated with {$organization.name} will be permanently + deleted. + This action is irreversible. + {/if} +

      -

      {#if $projects.total > 0} - The following projects and all data associated with {$organization.name} will - be permanently deleted. This action is irreversible. - {:else} - All data associated with {$organization.name} will be permanently deleted. - This action is irreversible. - {/if} -

      - - {#if $projects.total > 0} -
      - - {#each tabs as { name, label, total }} - (selectedTab = name)}> - {label.desktop} ({total}) - - {/each} - - - - - {#each tabData.headers as header} - {header} - {/each} - - - {#each tabData.rows as row} - - {#each row.cells as cell} - {cell} - {/each} - - {/each} - - -
      -
      - - {#each tabs as { name, label, total }} - (selectedTab = name)}> - {label.mobile} ({total}) - - {/each} - - - - - {#each tabData.headers as header, index} - {header} +
      + + {#each tabs as { name, label, total }} + (selectedTab = name)}> + {label.desktop} ({total}) + {/each} - - - {#each tabData.rows as row} - - {#each row.cells as cell, index} - {cell} - {/each} - + + + + + {#each tabData.headers as header} + {header} + {/each} + + + {#each tabData.rows as row} + + {#each row.cells as cell} + {cell} + {/each} + + {/each} + + +
      +
      + + {#each tabs as { name, label, total }} + (selectedTab = name)}> + {label.mobile} ({total}) + {/each} - - -
      - {/if} + + + + + {#each tabData.headers as header, index} + {header} + {/each} + + + {#each tabData.rows as row} + + {#each row.cells as cell, index} + {cell} + {/each} + + {/each} + + +
      + {/if} - - - + + + + {/if} diff --git a/src/routes/(console)/organization-[organization]/settings/invoicesTable.svelte b/src/routes/(console)/organization-[organization]/settings/invoicesTable.svelte new file mode 100644 index 0000000000..fccee4c6a2 --- /dev/null +++ b/src/routes/(console)/organization-[organization]/settings/invoicesTable.svelte @@ -0,0 +1,137 @@ + + + + + Due Date + Status + Amount Due + + + + {#each invoices as invoice, i} + {@const status = invoice.status} + + + {toLocaleDate(invoice.dueAt)} + + + {#if invoice?.lastError} + + + {status === 'requires_authentication' ? 'failed' : status} + + +
    • + The scheduled payment has failed. + + . +
    • +
      +
      + {:else} + + {status === 'requires_authentication' ? 'failed' : status} + + {/if} +
      + + {formatCurrency(invoice.grossAmount)} + + + + + + (showDropdown[i] = !showDropdown[i])} + event="view_invoice"> + View invoice + + { + showDropdown[i] = !showDropdown[i]; + }} + event="download_invoice"> + Download PDF + + {#if status === 'overdue' || status === 'failed'} + { + retryPayment(invoice); + showDropdown[i] = !showDropdown[i]; + trackEvent(`click_retry_payment`, { + from: 'button', + source: 'billing_invoice_menu' + }); + }}> + Retry payment + + {/if} + + + +
      + {/each} +
      +
      From 2f36ff286bc2d4aceec436af7ab3edefeccc2c09 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 20 Jan 2025 06:42:13 +0000 Subject: [PATCH 38/39] remove unused code --- .../(console)/organization-[organization]/settings/+page.svelte | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/routes/(console)/organization-[organization]/settings/+page.svelte b/src/routes/(console)/organization-[organization]/settings/+page.svelte index 361a52d73e..98b236e6d7 100644 --- a/src/routes/(console)/organization-[organization]/settings/+page.svelte +++ b/src/routes/(console)/organization-[organization]/settings/+page.svelte @@ -15,12 +15,10 @@ import { isCloud } from '$lib/system'; import Baa from './BAA.svelte'; import Soc2 from './Soc2.svelte'; - import DeleteOrganizationEstimation from './deleteOrganizationEstimation.svelte'; export let data; let name: string; let showDelete = false; - let showDeleteEstimation = false; onMount(() => { name = $organization.name; From 09ba6186d6c3a4949fd970c4ebe94304dde43b94 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 20 Jan 2025 08:26:04 +0000 Subject: [PATCH 39/39] fix check --- .../settings/deleteOrganizationModal.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/(console)/organization-[organization]/settings/deleteOrganizationModal.svelte b/src/routes/(console)/organization-[organization]/settings/deleteOrganizationModal.svelte index 26a3384917..96073a0460 100644 --- a/src/routes/(console)/organization-[organization]/settings/deleteOrganizationModal.svelte +++ b/src/routes/(console)/organization-[organization]/settings/deleteOrganizationModal.svelte @@ -124,7 +124,7 @@ icon="exclamation" state="warning" headerDivider={false}> - {#if estimation} + {#if estimation &&( estimation.unpaidInvoices.length > 0 || estimation.grossAmount > 0)} {:else}