From 5a2d85f3f08ac33bb429b1001093b742e7e3dabe Mon Sep 17 00:00:00 2001 From: Arman Date: Wed, 24 Apr 2024 18:05:38 +0200 Subject: [PATCH 01/30] feat: new org creation --- package-lock.json | 18 ++-- package.json | 4 +- src/lib/elements/forms/label.svelte | 5 +- src/lib/layout/index.ts | 4 + .../layout/wizardSecondaryContainer.svelte | 20 ++++ src/lib/layout/wizardSecondaryContent.svelte | 11 +++ src/lib/layout/wizardSecondaryFooter.svelte | 8 ++ src/lib/layout/wizardSecondaryHeader.svelte | 17 ++++ src/lib/stores/billing.ts | 2 +- .../console/create-organization/+page.svelte | 93 +++++++++++++++++++ .../console/create-organization/store.ts | 28 ++++++ 11 files changed, 197 insertions(+), 13 deletions(-) create mode 100644 src/lib/layout/wizardSecondaryContainer.svelte create mode 100644 src/lib/layout/wizardSecondaryContent.svelte create mode 100644 src/lib/layout/wizardSecondaryFooter.svelte create mode 100644 src/lib/layout/wizardSecondaryHeader.svelte create mode 100644 src/routes/console/create-organization/+page.svelte create mode 100644 src/routes/console/create-organization/store.ts diff --git a/package-lock.json b/package-lock.json index 2fc31fab0..2990b060e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,8 +7,8 @@ "name": "@appwrite/console", "dependencies": { "@appwrite.io/console": "^0.5.0", - "@appwrite.io/pink": "0.11.0", - "@appwrite.io/pink-icons": "0.11.0", + "@appwrite.io/pink": "0.16.0", + "@appwrite.io/pink-icons": "0.16.0", "@popperjs/core": "^2.11.8", "@sentry/svelte": "^7.66.0", "@sentry/tracing": "^7.66.0", @@ -167,19 +167,19 @@ } }, "node_modules/@appwrite.io/pink": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@appwrite.io/pink/-/pink-0.11.0.tgz", - "integrity": "sha512-0/ztJxeV6Cy1FnXy2ClLELzDnR4eRtGAhQ24napCg0VwOFevSIc0S7jcRrWVyA6WRuwex4eGiEn2JeYMTq70Xg==", + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@appwrite.io/pink/-/pink-0.16.0.tgz", + "integrity": "sha512-KprOd+36I0w6PC0x6NJclvyJNpCsjAPcrCHL3QixSX0dZ66wqFE6NitF/KiVVCxbuBaNgjrm7oQ0hr7dt1Sm5Q==", "dependencies": { - "@appwrite.io/pink-icons": "0.11.0", + "@appwrite.io/pink-icons": "0.16.0", "normalize.css": "^8.0.1", "the-new-css-reset": "^1.11.2" } }, "node_modules/@appwrite.io/pink-icons": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@appwrite.io/pink-icons/-/pink-icons-0.11.0.tgz", - "integrity": "sha512-FOvP87m4S17QRQPrLzsSlwrbIeJNJi5RliGUQYONIhmI0PiwE4Xm5LXFE+NqFYsR56w2X3jYdJIHdIdZlH3XjA==" + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@appwrite.io/pink-icons/-/pink-icons-0.16.0.tgz", + "integrity": "sha512-tWaG5lFXnKzbFbGf4SlxoFYj2yH7kQDejFq+UHdfzBrqteSfZpxIxdNmKHIea1ztcmsat4y+R0ZbVX9iOv+oPw==" }, "node_modules/@babel/code-frame": { "version": "7.23.5", diff --git a/package.json b/package.json index 863f04765..9ff2f2d46 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,8 @@ }, "dependencies": { "@appwrite.io/console": "^0.5.0", - "@appwrite.io/pink": "0.11.0", - "@appwrite.io/pink-icons": "0.11.0", + "@appwrite.io/pink": "0.16.0", + "@appwrite.io/pink-icons": "0.16.0", "@popperjs/core": "^2.11.8", "@sentry/svelte": "^7.66.0", "@sentry/tracing": "^7.66.0", diff --git a/src/lib/elements/forms/label.svelte b/src/lib/elements/forms/label.svelte index 17c1fb922..89adc3f70 100644 --- a/src/lib/elements/forms/label.svelte +++ b/src/lib/elements/forms/label.svelte @@ -6,6 +6,7 @@ hide?: boolean; tooltip?: string; for?: string; + class?: string; } export let required: $$Props['required'] = false; @@ -13,12 +14,14 @@ export let optionalText: $$Props['optionalText'] = undefined; export let hide: $$Props['hide'] = false; export let tooltip: $$Props['tooltip'] = null; + let classes: string = undefined; + export { classes as class }; diff --git a/src/lib/layout/index.ts b/src/lib/layout/index.ts index cf1bd6e4b..afc33bb2c 100644 --- a/src/lib/layout/index.ts +++ b/src/lib/layout/index.ts @@ -19,3 +19,7 @@ export { default as GridHeader } from './gridHeader.svelte'; export { default as ContainerHeader } from './containerHeader.svelte'; export { default as HeaderAlert } from './headerAlert.svelte'; export { default as ContainerButton } from './containerButton.svelte'; +export { default as WizardSecondaryContainer } from './wizardSecondaryContainer.svelte'; +export { default as WizardSecondaryContent } from './wizardSecondaryContent.svelte'; +export { default as WizardSecondaryHeader } from './wizardSecondaryHeader.svelte'; +export { default as WizardSecondaryFooter } from './wizardSecondaryFooter.svelte'; diff --git a/src/lib/layout/wizardSecondaryContainer.svelte b/src/lib/layout/wizardSecondaryContainer.svelte new file mode 100644 index 000000000..7952cbbe8 --- /dev/null +++ b/src/lib/layout/wizardSecondaryContainer.svelte @@ -0,0 +1,20 @@ +
+
+ +
+
+ + diff --git a/src/lib/layout/wizardSecondaryContent.svelte b/src/lib/layout/wizardSecondaryContent.svelte new file mode 100644 index 000000000..6b69d488a --- /dev/null +++ b/src/lib/layout/wizardSecondaryContent.svelte @@ -0,0 +1,11 @@ +
+
+ +
+
+
+
+ +
+
+
diff --git a/src/lib/layout/wizardSecondaryFooter.svelte b/src/lib/layout/wizardSecondaryFooter.svelte new file mode 100644 index 000000000..69e3f8667 --- /dev/null +++ b/src/lib/layout/wizardSecondaryFooter.svelte @@ -0,0 +1,8 @@ +
+
+ +
+
+ +
+
diff --git a/src/lib/layout/wizardSecondaryHeader.svelte b/src/lib/layout/wizardSecondaryHeader.svelte new file mode 100644 index 000000000..e7971f533 --- /dev/null +++ b/src/lib/layout/wizardSecondaryHeader.svelte @@ -0,0 +1,17 @@ + + +
+
+ + +
+ {#if $$slots.description} +

+ {/if} +
diff --git a/src/lib/stores/billing.ts b/src/lib/stores/billing.ts index 81675b4f6..678a10138 100644 --- a/src/lib/stores/billing.ts +++ b/src/lib/stores/billing.ts @@ -114,7 +114,7 @@ export type TierData = { export const tierFree: TierData = { name: 'Starter', - description: 'For personal, passion projects.' + description: 'For personal hobby projects of small scale and students.' }; export const tierPro: TierData = { diff --git a/src/routes/console/create-organization/+page.svelte b/src/routes/console/create-organization/+page.svelte new file mode 100644 index 000000000..2a16c4eef --- /dev/null +++ b/src/routes/console/create-organization/+page.svelte @@ -0,0 +1,93 @@ + + + + Create organization + +
console.log('test')}> + + + + +
    +
  • + + +
    +

    + {tierFree.name} +

    +

    {tierFree.description}

    +

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

    +
    +
    +
    +
  • + +
  • + + +
    +

    + {tierPro.name} +

    +

    + {tierPro.description} +

    +

    + {formatCurrency(proPlan?.price ?? 0)} per member/month + usage +

    +
    +
    +
    +
  • +
+
+ +
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Recusandae, incidunt! +
+
+
+ + + + + +
diff --git a/src/routes/console/create-organization/store.ts b/src/routes/console/create-organization/store.ts new file mode 100644 index 000000000..613259fe0 --- /dev/null +++ b/src/routes/console/create-organization/store.ts @@ -0,0 +1,28 @@ +import { BillingPlan } from '$lib/constants'; +import type { WizardStepsType } from '$lib/layout/wizard.svelte'; +import type { Tier } from '$lib/stores/billing'; +import { writable } from 'svelte/store'; + +export const createOrgSteps = writable(new Map()); +export const createOrganizationFinalAction = writable('Start trial'); + +export const createOrganization = writable<{ + id?: string; + name: string; + billingPlan: Tier; + paymentMethodId: string; + billingAddressId: string; + collaborators?: string[]; + billingBudget?: number; + taxId?: string; + couponCode?: string; +}>({ + id: null, + name: null, + billingPlan: BillingPlan.PRO, + paymentMethodId: null, + collaborators: [], + billingAddressId: null, + taxId: null, + couponCode: null +}); From 4ac90c367ac3888afb2a2aee756afb760a94bfbd Mon Sep 17 00:00:00 2001 From: Arman Date: Fri, 3 May 2024 13:59:18 +0200 Subject: [PATCH 02/30] chore: remove imports --- .../billing/planSummary.svelte | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/routes/console/organization-[organization]/billing/planSummary.svelte b/src/routes/console/organization-[organization]/billing/planSummary.svelte index 0fe63866e..fd3ef4b62 100644 --- a/src/routes/console/organization-[organization]/billing/planSummary.svelte +++ b/src/routes/console/organization-[organization]/billing/planSummary.svelte @@ -1,13 +1,6 @@ + +{#if filteredMethods?.length} + { + return { + value: method.$id, + label: `ending in ${method.last4}`, + data: [method.brand] + }; + })} + interactiveOutput + let:option={o}> + + + + {o.data} {o.label} + + + + + +{:else} + +
+

+ + No saved payment methods +

+ +
+
+{/if} + +{#if showPaymentModal && isCloud && hasStripePublicKey} + +{/if} diff --git a/src/lib/components/card.svelte b/src/lib/components/card.svelte index d5a3cbdb7..378aea133 100644 --- a/src/lib/components/card.svelte +++ b/src/lib/components/card.svelte @@ -5,6 +5,8 @@ isTile?: boolean; isDashed?: boolean; danger?: boolean; + style?: string; + class?: string; }; type ButtonProps = { @@ -26,6 +28,7 @@ export let href: string = null; let classes = ''; export { classes as class }; + export let style: string = ''; function getElement() { switch (true) { @@ -46,6 +49,7 @@ class:is-border-dashed={isDashed} class:is-danger={danger} class:is-allowed-focus={href} + {style} on:click on:keyup={clickOnEnter} role={href || isButton ? 'button' : 'generic'} diff --git a/src/lib/elements/forms/inputSelectSearch.svelte b/src/lib/elements/forms/inputSelectSearch.svelte index 6d1fad7df..ec42f2287 100644 --- a/src/lib/elements/forms/inputSelectSearch.svelte +++ b/src/lib/elements/forms/inputSelectSearch.svelte @@ -8,7 +8,7 @@ type Option = $$Generic<{ value: string | boolean | number; label: string; - data?: string[]; + data?: unknown[]; }>; type OptionArray = Option[]; diff --git a/src/lib/elements/forms/inputTags.svelte b/src/lib/elements/forms/inputTags.svelte index 2d3caac21..c6b088ede 100644 --- a/src/lib/elements/forms/inputTags.svelte +++ b/src/lib/elements/forms/inputTags.svelte @@ -13,6 +13,8 @@ export let readonly = false; export let required = false; export let tooltip: string = null; + export let validityRegex: RegExp = null; + export let validityMessage: string = null; let value = ''; let element: HTMLInputElement; @@ -32,8 +34,13 @@ if (value === '' && ['Enter', 'Tab'].includes(e.key)) { return; } + if (['Enter', 'Tab', ' '].includes(e.key)) { e.preventDefault(); + if (validityRegex && !validityRegex.test(value)) { + error = validityMessage ? validityMessage : 'Invalid value'; + return; + } addValue(); } if (['Backspace', 'Delete'].includes(e.key)) { diff --git a/src/lib/elements/forms/label.svelte b/src/lib/elements/forms/label.svelte index 89adc3f70..5601770b6 100644 --- a/src/lib/elements/forms/label.svelte +++ b/src/lib/elements/forms/label.svelte @@ -1,4 +1,6 @@ + + Create organization - Appwrite + + Create organization
console.log('test')}> - + +

+ For more details on our plans, visit our + . +

  • - +

    @@ -78,6 +107,22 @@

+ {#if billingPlan === BillingPlan.PRO} + + + + + + {/if}
From 4550a77839aa256dbd30bf69d3d929d47fa1640c Mon Sep 17 00:00:00 2001 From: Arman Date: Mon, 6 May 2024 17:56:47 +0200 Subject: [PATCH 04/30] chore: Update import paths for payment methods and payment modal components --- .../payments => lib/components/billing}/paymentModal.svelte | 0 src/routes/console/account/payments/paymentMethods.svelte | 2 +- .../organization-[organization]/billing/paymentMethods.svelte | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename src/{routes/console/account/payments => lib/components/billing}/paymentModal.svelte (100%) diff --git a/src/routes/console/account/payments/paymentModal.svelte b/src/lib/components/billing/paymentModal.svelte similarity index 100% rename from src/routes/console/account/payments/paymentModal.svelte rename to src/lib/components/billing/paymentModal.svelte diff --git a/src/routes/console/account/payments/paymentMethods.svelte b/src/routes/console/account/payments/paymentMethods.svelte index e8a210ae6..d03911a40 100644 --- a/src/routes/console/account/payments/paymentMethods.svelte +++ b/src/routes/console/account/payments/paymentMethods.svelte @@ -25,7 +25,7 @@ import EditPaymentModal from './editPaymentModal.svelte'; import DeletePaymentModal from './deletePaymentModal.svelte'; import { hasStripePublicKey, isCloud } from '$lib/system'; - import PaymentModal from './paymentModal.svelte'; + import PaymentModal from '$lib/components/billing/paymentModal.svelte'; export let showPayment = false; let showDropdown = []; diff --git a/src/routes/console/organization-[organization]/billing/paymentMethods.svelte b/src/routes/console/organization-[organization]/billing/paymentMethods.svelte index cf9e8e4f5..6df295ebf 100644 --- a/src/routes/console/organization-[organization]/billing/paymentMethods.svelte +++ b/src/routes/console/organization-[organization]/billing/paymentMethods.svelte @@ -14,7 +14,6 @@ import { addNotification } from '$lib/stores/notifications'; import { organization } from '$lib/stores/organization'; import { Button } from '$lib/elements/forms'; - import PaymentModal from '$routes/console/account/payments/paymentModal.svelte'; import { hasStripePublicKey, isCloud } from '$lib/system'; import { paymentMethods } from '$lib/stores/billing'; import type { PaymentMethodData } from '$lib/sdk/billing'; @@ -22,6 +21,7 @@ import ReplaceCard from './replaceCard.svelte'; import EditPaymentModal from '$routes/console/account/payments/editPaymentModal.svelte'; import { tooltip } from '$lib/actions/tooltip'; + import PaymentModal from '$lib/components/billing/paymentModal.svelte'; let showDropdown = false; let showDropdownBackup = false; From 76b2ba4f93156d099cf081dbff7846ffb07e0f26 Mon Sep 17 00:00:00 2001 From: Arman Date: Mon, 6 May 2024 17:57:30 +0200 Subject: [PATCH 05/30] chore: Add end slot to inputSelectSearch and update import paths for payment methods and payment modal components --- .../billing/selectPaymentMethod.svelte | 9 +- .../billing/validateCreditModal.svelte | 57 +++++ src/lib/elements/forms/inputChoice.svelte | 12 +- .../elements/forms/inputSelectSearch.svelte | 11 + .../console/create-organization/+page.svelte | 230 ++++++++++++++++-- 5 files changed, 292 insertions(+), 27 deletions(-) create mode 100644 src/lib/components/billing/validateCreditModal.svelte diff --git a/src/lib/components/billing/selectPaymentMethod.svelte b/src/lib/components/billing/selectPaymentMethod.svelte index bb5c637d9..97f32e2ef 100644 --- a/src/lib/components/billing/selectPaymentMethod.svelte +++ b/src/lib/components/billing/selectPaymentMethod.svelte @@ -3,9 +3,9 @@ import type { PaymentList, PaymentMethodData } from '$lib/sdk/billing'; import { sdk } from '$lib/stores/sdk'; import { hasStripePublicKey, isCloud } from '$lib/system'; - import PaymentModal from '$routes/console/account/payments/paymentModal.svelte'; import { onMount } from 'svelte'; import { Card, CreditCardBrandImage } from '..'; + import PaymentModal from './paymentModal.svelte'; export let methods: PaymentList; export let value: string; @@ -30,6 +30,7 @@ { return { @@ -48,6 +49,12 @@ + + + {:else} diff --git a/src/lib/components/billing/validateCreditModal.svelte b/src/lib/components/billing/validateCreditModal.svelte new file mode 100644 index 000000000..961e04144 --- /dev/null +++ b/src/lib/components/billing/validateCreditModal.svelte @@ -0,0 +1,57 @@ + + + + Credits will be applied automatically to your next invoice. + + + + + + + + + + diff --git a/src/lib/elements/forms/inputChoice.svelte b/src/lib/elements/forms/inputChoice.svelte index 21d2b48f1..c659e8320 100644 --- a/src/lib/elements/forms/inputChoice.svelte +++ b/src/lib/elements/forms/inputChoice.svelte @@ -1,4 +1,5 @@ @@ -53,9 +150,14 @@ Create organization -
console.log('test')}> + - +

@@ -110,6 +212,7 @@ {#if billingPlan === BillingPlan.PRO} - + {#if !couponData?.code} + + {/if} {/if}

-
- Lorem ipsum dolor sit amet consectetur adipisicing elit. Recusandae, incidunt! -
+ {#if billingPlan !== BillingPlan.STARTER} + + +

{currentPlan.name} plan

+

{formatCurrency(currentPlan.price)}

+
+ +

Additional seats ({collaborators?.length})

+

+ {formatCurrency(extraSeatsCost)} +

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

+

+ +
+

+ Credits applied +

+
+ {/if} +
+ +

Estimated total

+

+ {formatCurrency(estimatedTotal)} +

+
+ + {@const trialEndDate = new Date( + billingPayDate.getTime() + currentPlan.trialDays * 24 * 60 * 60 * 1000 + )} +

+ Your payment method will be charged this amount plus usage fees every 30 + days {!currentPlan.trialDays + ? `starting ${toLocaleDate(billingPayDate.toString())}` + : ` after your trial period ends on ${toLocaleDate(trialEndDate.toString())}`}. +

+ + + {#if budgetEnabled} +
+ +
+ {/if} +
+
+ + {/if} - - + + + + From 5894eb8885f9bd5b92bfe37bbc4ca7082cccb632 Mon Sep 17 00:00:00 2001 From: Arman Date: Tue, 7 May 2024 16:10:05 +0200 Subject: [PATCH 06/30] =?UTF-8?q?feat:=20create=20org=20wizard=20=E2=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/billing/paymentModal.svelte | 1 + .../billing/selectPaymentMethod.svelte | 39 +++++- src/lib/elements/forms/form.svelte | 12 +- .../elements/forms/inputSelectSearch.svelte | 4 +- .../console/create-organization/+page.svelte | 123 +++++++++++------- .../console/create-organization/store.ts | 28 ---- 6 files changed, 126 insertions(+), 81 deletions(-) delete mode 100644 src/routes/console/create-organization/store.ts diff --git a/src/lib/components/billing/paymentModal.svelte b/src/lib/components/billing/paymentModal.svelte index 2ac93e0b2..2b5ef5d1b 100644 --- a/src/lib/components/billing/paymentModal.svelte +++ b/src/lib/components/billing/paymentModal.svelte @@ -70,6 +70,7 @@ + - import { Button, InputSelectSearch } from '$lib/elements/forms'; + import { Button, InputChoice, InputSelectSearch, InputText } from '$lib/elements/forms'; import type { PaymentList, PaymentMethodData } from '$lib/sdk/billing'; import { sdk } from '$lib/stores/sdk'; import { hasStripePublicKey, isCloud } from '$lib/system'; import { onMount } from 'svelte'; import { Card, CreditCardBrandImage } from '..'; import PaymentModal from './paymentModal.svelte'; + import { capitalize } from '$lib/helpers/string'; export let methods: PaymentList; export let value: string; + export let taxId = ''; + let showTaxId = false; let showPaymentModal = false; async function cardSaved(event: CustomEvent) { @@ -35,21 +38,34 @@ options={filteredMethods.map((method) => { return { value: method.$id, - label: `ending in ${method.last4}`, + label: `${capitalize(method.brand)} ending in ${method.last4}`, data: [method.brand] }; })} interactiveOutput let:option={o}> + + + + + + {o.label} + + + + + + + - {o.data} {o.label} + {o.label} - +
-

- Credits applied -

+ {#if couponData.credits > 100} +

+ Credits applied +

+ {:else} + -{formatCurrency(couponData.credits)} + {/if} {/if}
@@ -298,7 +326,7 @@ @@ -321,7 +349,12 @@ - + diff --git a/src/routes/console/create-organization/store.ts b/src/routes/console/create-organization/store.ts deleted file mode 100644 index 613259fe0..000000000 --- a/src/routes/console/create-organization/store.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { BillingPlan } from '$lib/constants'; -import type { WizardStepsType } from '$lib/layout/wizard.svelte'; -import type { Tier } from '$lib/stores/billing'; -import { writable } from 'svelte/store'; - -export const createOrgSteps = writable(new Map()); -export const createOrganizationFinalAction = writable('Start trial'); - -export const createOrganization = writable<{ - id?: string; - name: string; - billingPlan: Tier; - paymentMethodId: string; - billingAddressId: string; - collaborators?: string[]; - billingBudget?: number; - taxId?: string; - couponCode?: string; -}>({ - id: null, - name: null, - billingPlan: BillingPlan.PRO, - paymentMethodId: null, - collaborators: [], - billingAddressId: null, - taxId: null, - couponCode: null -}); From 3be52e368636af6f095014863627b0dc42d49ba1 Mon Sep 17 00:00:00 2001 From: Arman Date: Tue, 7 May 2024 17:43:05 +0200 Subject: [PATCH 07/30] feat: replace creation wizard in console, add search param logic --- src/lib/layout/header.svelte | 3 +-- .../account/organizations/+page.svelte | 5 ++--- .../console/create-organization/+page.svelte | 22 +++++++++++++++++++ src/routes/console/onboarding/+page.svelte | 11 +++------- .../organization-[organization]/+page.svelte | 3 +-- .../organization-[organization]/header.svelte | 5 ++--- 6 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/lib/layout/header.svelte b/src/lib/layout/header.svelte index 16c97001a..3e5168234 100644 --- a/src/lib/layout/header.svelte +++ b/src/lib/layout/header.svelte @@ -21,7 +21,6 @@ import { sdk } from '$lib/stores/sdk'; import { isCloud } from '$lib/system'; import { wizard } from '$lib/stores/wizard'; - import CreateOrganizationCloud from '$routes/console/createOrganizationCloud.svelte'; import { Feedback } from '$lib/components/feedback'; import ChangeOrganizationTierCloud from '$routes/console/changeOrganizationTierCloud.svelte'; import { BillingPlan } from '$lib/constants'; @@ -56,7 +55,7 @@ function createOrg() { showDropdown = false; if (isCloud) { - wizard.start(CreateOrganizationCloud); + goto(`${base}/console/create-organization`); } else newOrgModal.set(true); } diff --git a/src/routes/console/account/organizations/+page.svelte b/src/routes/console/account/organizations/+page.svelte index 33811a134..08667a5b1 100644 --- a/src/routes/console/account/organizations/+page.svelte +++ b/src/routes/console/account/organizations/+page.svelte @@ -20,9 +20,8 @@ import { daysLeftInTrial, plansInfo } from '$lib/stores/billing'; import { tooltip } from '$lib/actions/tooltip'; import { toLocaleDate } from '$lib/helpers/date'; - import { wizard } from '$lib/stores/wizard'; - import CreateOrganizationCloud from '$routes/console/createOrganizationCloud.svelte'; import { BillingPlan } from '$lib/constants'; + import { goto } from '$app/navigation'; export let data: PageData; let addOrganization = false; @@ -40,7 +39,7 @@ function createOrg() { if (isCloud) { - wizard.start(CreateOrganizationCloud); + goto(`${base}/console/create-organization`); } else addOrganization = true; } diff --git a/src/routes/console/create-organization/+page.svelte b/src/routes/console/create-organization/+page.svelte index b8d8bbc4f..96a074696 100644 --- a/src/routes/console/create-organization/+page.svelte +++ b/src/routes/console/create-organization/+page.svelte @@ -61,6 +61,28 @@ let showCreditModal = false; onMount(async () => { + 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 + }; + } + } + if ($page.url.searchParams.has('name')) { + name = $page.url.searchParams.get('name'); + } + if ($page.url.searchParams.has('plan')) { + const plan = $page.url.searchParams.get('plan'); + if (plan && plan in BillingPlan) { + billingPlan = plan as BillingPlan; + } + } if (anyOrgFree) { billingPlan = BillingPlan.PRO; } diff --git a/src/routes/console/onboarding/+page.svelte b/src/routes/console/onboarding/+page.svelte index af2a1e2ba..1f7aac074 100644 --- a/src/routes/console/onboarding/+page.svelte +++ b/src/routes/console/onboarding/+page.svelte @@ -11,14 +11,12 @@ import { Container } from '$lib/layout'; import { addNotification } from '$lib/stores/notifications'; import { sdk } from '$lib/stores/sdk'; - import { wizard } from '$lib/stores/wizard'; import { isCloud } from '$lib/system'; import { ID } from '@appwrite.io/console'; import { onMount } from 'svelte'; - import CreateOrganizationCloud from '../createOrganizationCloud.svelte'; import { tierToPlan, type Tier, plansInfo } from '$lib/stores/billing'; - import { createOrganization } from '../wizard/cloudOrganization/store'; import { formatCurrency } from '$lib/helpers/numbers'; + import { base } from '$app/paths'; let name: string; let id: string; @@ -43,7 +41,7 @@ if ($page.url.searchParams.has('type')) { const paramType = $page.url.searchParams.get('type'); if (paramType === 'createPro') { - wizard.start(CreateOrganizationCloud); + goto(`${base}/console/create-organization`); } } } @@ -79,10 +77,7 @@ trackError(error, Submit.OrganizationCreate); } } else { - wizard.start(CreateOrganizationCloud, null, 2); - $createOrganization.name = orgName; - $createOrganization.billingPlan = plan; - $createOrganization.id = id; + goto(`${base}/console/create-organization?name=${orgName}&plan=${plan}`); } } else { try { diff --git a/src/routes/console/organization-[organization]/+page.svelte b/src/routes/console/organization-[organization]/+page.svelte index 7fa8efc52..95dd694b2 100644 --- a/src/routes/console/organization-[organization]/+page.svelte +++ b/src/routes/console/organization-[organization]/+page.svelte @@ -30,7 +30,6 @@ import { readOnly } from '$lib/stores/billing'; import type { RegionList } from '$lib/sdk/billing'; import { onMount } from 'svelte'; - import CreateOrganizationCloud from '../createOrganizationCloud.svelte'; import { organization } from '$lib/stores/organization'; export let data; @@ -125,7 +124,7 @@ if ($page.url.searchParams.has('type')) { const paramType = $page.url.searchParams.get('type'); if (paramType === 'createPro') { - wizard.start(CreateOrganizationCloud); + goto(`${base}/console/create-organization`); } } } diff --git a/src/routes/console/organization-[organization]/header.svelte b/src/routes/console/organization-[organization]/header.svelte index 9536290af..4450fc03e 100644 --- a/src/routes/console/organization-[organization]/header.svelte +++ b/src/routes/console/organization-[organization]/header.svelte @@ -1,4 +1,5 @@ @@ -58,12 +57,12 @@ Usage on the Starter plan is limited for the following resources. Next billing period: {toLocaleDate( nextDate )}. - {:else if tier === BillingPlan.PRO} + {:else if org.billingPlan === BillingPlan.PRO}

Usage on the Pro plan will be charged at the end of each billing period at the following rates. Next billing period: {toLocaleDate(nextDate)}.

- {:else if tier === BillingPlan.SCALE} + {:else if org.billingPlan === BillingPlan.SCALE}

Usage on the Scale plan will be charged at the end of each billing period at the following rates. Next billing period: {toLocaleDate(nextDate)}. diff --git a/src/routes/console/+layout.svelte b/src/routes/console/+layout.svelte index 6c9a44291..dc8ceeb81 100644 --- a/src/routes/console/+layout.svelte +++ b/src/routes/console/+layout.svelte @@ -38,10 +38,9 @@ import { stripe } from '$lib/stores/stripe'; import MobileSupportModal from './wizard/support/mobileSupportModal.svelte'; import { showSupportModal } from './wizard/support/store'; - - import UsageRates from './wizard/cloudOrganization/usageRates.svelte'; import { activeHeaderAlert, consoleVariables } from './store'; import { headerAlert } from '$lib/stores/headerAlert'; + import { UsageRates } from '$lib/components/billing'; function kebabToSentenceCase(str: string) { return str @@ -320,5 +319,5 @@ {/if} {#if isCloud && $showUsageRatesModal} - + {/if} diff --git a/src/routes/console/createOrganizationCloud.svelte b/src/routes/console/createOrganizationCloud.svelte deleted file mode 100644 index 459e4b77f..000000000 --- a/src/routes/console/createOrganizationCloud.svelte +++ /dev/null @@ -1,149 +0,0 @@ - - - diff --git a/src/routes/console/wizard/cloudOrganization/addressDetails.svelte b/src/routes/console/wizard/cloudOrganization/addressDetails.svelte deleted file mode 100644 index 00439227e..000000000 --- a/src/routes/console/wizard/cloudOrganization/addressDetails.svelte +++ /dev/null @@ -1,144 +0,0 @@ - - - - Billing address - Add a billing address for your organization. - - - {#if addressList} - - -

-

{element.streetAddress}

- {#if element?.addressLine2} -

{element.addressLine2}

- {/if} -

{element.city}

-

{element.state}

-

{element.postalCode}

-

{element.country}

-
- - - - Add new billing address - - - - - - - - - - - - - - - {/if} - - diff --git a/src/routes/console/wizard/cloudOrganization/confirmDetails.svelte b/src/routes/console/wizard/cloudOrganization/confirmDetails.svelte deleted file mode 100644 index e3eb21d55..000000000 --- a/src/routes/console/wizard/cloudOrganization/confirmDetails.svelte +++ /dev/null @@ -1,138 +0,0 @@ - - - - Details - - Confirm the details of your new organization{!plan.trialDays - ? '.' - : ' and start your free trial.'} - - -

Organization name

-
-

{$createOrganization.name}

- {#if $createOrganization?.id} - {$createOrganization.id} - {/if} -
- - {#if $createOrganization.billingPlan !== BillingPlan.STARTER} -
-

Additional members

-

{collaboratorsNumber} members

-
-
-

Payment

- {#await fetchCard()} -
-
-
- {:then card} - -

- {card?.brand} ending in {card?.last4} -

- -
- {/await} -
- {/if} - - {#if $createOrganization.billingPlan !== BillingPlan.STARTER} - ($createOrganization.couponCode = e.detail.code)} /> - -

{plan.name} plan

-

{formatCurrency(plan.price)}

-
- -

Additional members ({collaboratorsNumber})

-

{formatCurrency(collaboratorPrice * collaboratorsNumber)}

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

Credits applied ({couponData.credits})

-

-{formatCurrency(couponData.credits)}

-
- {/if} -
- {/if} - {@const estimatedTotal = - couponData?.status === 'active' - ? totalExpences - couponData.credits >= 0 - ? totalExpences - couponData.credits - : 0 - : totalExpences} - -

Estimated total

-

- {formatCurrency(estimatedTotal)} -

-
- - {#if $createOrganization.billingPlan !== BillingPlan.STARTER} -

- This amount, and any additional usage fees, will be charged on a recurring 30-day - billing cycle{!plan.trialDays - ? '' - : ` after your trial period ends on ${toLocaleDate(billingPayDate.toString())}`}. -

- {/if} - - diff --git a/src/routes/console/wizard/cloudOrganization/inviteMembers.svelte b/src/routes/console/wizard/cloudOrganization/inviteMembers.svelte deleted file mode 100644 index 1bfe98fc6..000000000 --- a/src/routes/console/wizard/cloudOrganization/inviteMembers.svelte +++ /dev/null @@ -1,103 +0,0 @@ - - - - Invites - - Invite team members to collaborate with you in the Appwrite console. Members will have - access to all services and payment data within your organization. - - - - {#if $createOrganization.billingPlan === BillingPlan.SCALE} - You can add unlimited organization members on the {plan.name} plan at no cost. Each member - added will receive an email invite to your organization on completion. - {:else if $createOrganization.billingPlan === BillingPlan.PRO} - You can add unlimited organization members on the {plan.name} plan for - {formatCurrency(plan.addons.member.price)} each per month. Each member added will - receive an email invite to your organization on completion. - {/if} - - -
- - - - - - - -
- - {#if $createOrganization?.collaborators?.length} -
- - - Collaborator - {#if $createOrganization.billingPlan === BillingPlan.PRO} - Cost - {/if} - - - - {#each $createOrganization.collaborators as collaborator} - - {collaborator} - {#if $createOrganization.billingPlan === BillingPlan.PRO} - {formatCurrency(15)} - {/if} - - - - - {/each} - -
-
- {/if} -
diff --git a/src/routes/console/wizard/cloudOrganization/organizationDetails.svelte b/src/routes/console/wizard/cloudOrganization/organizationDetails.svelte deleted file mode 100644 index 67bed9a75..000000000 --- a/src/routes/console/wizard/cloudOrganization/organizationDetails.svelte +++ /dev/null @@ -1,142 +0,0 @@ - - - - Organization details - - - - {#if !showCustomId} -
- (showCustomId = !showCustomId)}> - -
- {:else} - - {/if} -
- -

Plan

-

- For more details on our plans, visit our pricing page. -

-
    -
  • - - -
    -

    - {tierFree.name} - {formatCurrency(freePlan?.price ?? 0)}/month -

    -

    {tierFree.description}

    -
    -
    -
    -
  • - -
  • - - -
    -

    - {tierPro.name} - {formatCurrency(proPlan?.price ?? 0)}/month per - organization member + exta usage -

    -

    - {tierPro.description} -

    -
    - {#if proPlan?.trialDays} - 14 DAY FREE TRIAL - {/if} -
    -
    -
  • -
  • - - -
    -

    - {tierScale.name} - {formatCurrency(scalePlan?.price ?? 0)}/month + extra - usage -

    -

    - {tierScale.description} -

    -
    -
    - COMING SOON -
    -
    -
    -
  • -
-
diff --git a/src/routes/console/wizard/cloudOrganization/paymentDetails.svelte b/src/routes/console/wizard/cloudOrganization/paymentDetails.svelte deleted file mode 100644 index 956064b66..000000000 --- a/src/routes/console/wizard/cloudOrganization/paymentDetails.svelte +++ /dev/null @@ -1,117 +0,0 @@ - - - - Payment details - - Add a payment method to your organization. {#if $plansInfo.get($createOrganization.billingPlan)?.trialDays}You - will not be charged until your trial ends on {toLocaleDate(billingPayDate.toString())}. - {/if} - - - - - - -

- Restrict your resource usage by setting a budget cap. . -

- {#if budgetEnabled} -
- -
- {/if} -
-
-
diff --git a/src/routes/console/wizard/cloudOrganization/store.ts b/src/routes/console/wizard/cloudOrganization/store.ts deleted file mode 100644 index 55a53175c..000000000 --- a/src/routes/console/wizard/cloudOrganization/store.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { BillingPlan } from '$lib/constants'; -import type { WizardStepsType } from '$lib/layout/wizard.svelte'; -import { type Tier } from '$lib/stores/billing'; -import { writable } from 'svelte/store'; - -export const createOrgSteps = writable(new Map()); -export const createOrganizationFinalAction = writable('Create'); - -export const createOrganization = writable<{ - id?: string; - name: string; - billingPlan: Tier; - paymentMethodId: string; - billingAddressId: string; - collaborators?: string[]; - billingBudget?: number; - taxId?: string; - couponCode?: string; -}>({ - id: null, - name: null, - billingPlan: BillingPlan.PRO, - paymentMethodId: null, - collaborators: [], - billingAddressId: null, - taxId: null, - couponCode: null -}); From 07db6cd4e2569172ccbc341994f10ec34f6514e7 Mon Sep 17 00:00:00 2001 From: Arman Date: Wed, 8 May 2024 14:59:54 +0200 Subject: [PATCH 10/30] feat: add comparison box --- package-lock.json | 4 +- .../billing/estimatedTotalBox.svelte | 114 +++++++++++++++ src/lib/components/billing/index.ts | 2 + .../billing/planComparisonBox.svelte | 50 +++++++ src/lib/components/secondaryTabs.svelte | 3 +- .../console/create-organization/+page.svelte | 133 ++---------------- 6 files changed, 185 insertions(+), 121 deletions(-) create mode 100644 src/lib/components/billing/estimatedTotalBox.svelte create mode 100644 src/lib/components/billing/planComparisonBox.svelte diff --git a/package-lock.json b/package-lock.json index 328b25876..f9918446f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,8 +7,8 @@ "name": "@appwrite/console", "dependencies": { "@appwrite.io/console": "^0.6.1", - "@appwrite.io/pink": "0.11.0", - "@appwrite.io/pink-icons": "0.11.0", + "@appwrite.io/pink": "0.16.0", + "@appwrite.io/pink-icons": "0.16.0", "@popperjs/core": "^2.11.8", "@sentry/svelte": "^7.66.0", "@sentry/tracing": "^7.66.0", diff --git a/src/lib/components/billing/estimatedTotalBox.svelte b/src/lib/components/billing/estimatedTotalBox.svelte new file mode 100644 index 000000000..a20d951b2 --- /dev/null +++ b/src/lib/components/billing/estimatedTotalBox.svelte @@ -0,0 +1,114 @@ + + + + +

{currentPlan.name} plan

+

{formatCurrency(currentPlan.price)}

+
+ +

Additional seats ({collaborators?.length})

+

+ {formatCurrency(extraSeatsCost)} +

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

+

+ +
+ {#if couponData.credits > 100} +

+ Credits applied +

+ {:else} + -{formatCurrency(couponData.credits)} + {/if} +
+ {/if} +
+ +

Estimated total

+

+ {formatCurrency(estimatedTotal)} +

+
+ + {@const trialEndDate = new Date( + billingPayDate.getTime() + currentPlan.trialDays * 24 * 60 * 60 * 1000 + )} +

+ Your payment method will be charged this amount plus usage fees every 30 days {!currentPlan.trialDays + ? `starting ${toLocaleDate(billingPayDate.toString())}` + : ` after your trial period ends on ${toLocaleDate(trialEndDate.toString())}`}. +

+ + + {#if budgetEnabled} +
+ +
+ {/if} +
+
+ diff --git a/src/lib/components/billing/index.ts b/src/lib/components/billing/index.ts index 7fb76c11d..83da97477 100644 --- a/src/lib/components/billing/index.ts +++ b/src/lib/components/billing/index.ts @@ -2,3 +2,5 @@ export { default as PaymentBoxes } from './paymentBoxes.svelte'; export { default as CouponInput } from './couponInput.svelte'; export { default as SelectPaymentMethod } from './selectPaymentMethod.svelte'; export { default as UsageRates } from './usageRates.svelte'; +export { default as EstimatedTotalBox } from './estimatedTotalBox.svelte'; +export { default as PlanComparisonBox } from './planComparisonBox.svelte'; diff --git a/src/lib/components/billing/planComparisonBox.svelte b/src/lib/components/billing/planComparisonBox.svelte new file mode 100644 index 000000000..d9ae9326e --- /dev/null +++ b/src/lib/components/billing/planComparisonBox.svelte @@ -0,0 +1,50 @@ + + + + + (selectedTab = 'tier-0')}> + Starter + + (selectedTab = 'tier-1')}> + Pro + + + +
+ {#if selectedTab === 'tier-0'} +

{plan.name} plan

+
    +
  • + Limited to {plan.databases} Database, {plan.buckets} Buckets, {plan.functions} Functions + per project +
  • +
  • Limited to 1 organization member
  • +
  • {plan.bandwidth}GB bandwidth
  • +
  • {plan.storage}GB storage
  • +
  • {formatNum(plan.executions)} executions
  • +
+ {:else if selectedTab === 'tier-1'} +

{plan.name} plan

+
    +
  • Everything in the Starter plan, plus:
  • +
  • Unlimited databases, buckets, functions
  • +
  • {plan.bandwidth}GB bandwidth
  • +
  • {plan.storage}GB storage
  • +
  • {formatNum(plan.executions)} executions
  • +
  • Email support
  • +
+ {/if} +
+
diff --git a/src/lib/components/secondaryTabs.svelte b/src/lib/components/secondaryTabs.svelte index 4cc2a0db6..835f1a1cc 100644 --- a/src/lib/components/secondaryTabs.svelte +++ b/src/lib/components/secondaryTabs.svelte @@ -1,9 +1,10 @@ -
    +
    diff --git a/src/routes/console/create-organization/+page.svelte b/src/routes/console/create-organization/+page.svelte index 546fde48e..29a1b8d58 100644 --- a/src/routes/console/create-organization/+page.svelte +++ b/src/routes/console/create-organization/+page.svelte @@ -3,22 +3,15 @@ import { base } from '$app/paths'; import { page } from '$app/stores'; import { Submit, trackError, trackEvent } from '$lib/actions/analytics'; - import { tooltip } from '$lib/actions/tooltip'; - import { Box, LabelCard } from '$lib/components'; - import { SelectPaymentMethod } from '$lib/components/billing'; + import { LabelCard } from '$lib/components'; + import { + EstimatedTotalBox, + PlanComparisonBox, + SelectPaymentMethod + } from '$lib/components/billing'; import ValidateCreditModal from '$lib/components/billing/validateCreditModal.svelte'; import { BillingPlan, Dependencies } from '$lib/constants'; - import { - Button, - Form, - FormList, - InputChoice, - InputNumber, - InputTags, - InputText, - Label - } from '$lib/elements/forms'; - import { toLocaleDate } from '$lib/helpers/date'; + import { Button, Form, FormList, InputTags, InputText, Label } from '$lib/elements/forms'; import { formatCurrency } from '$lib/helpers/numbers'; import { WizardSecondaryContainer, @@ -39,9 +32,6 @@ (org) => (org as Organization)?.billingPlan === BillingPlan.STARTER ); const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,63}$/i; - const today = new Date(); - const billingPayDate = new Date(today.getTime() + 30 * 24 * 60 * 60 * 1000); - let previousPage: string = `${base}/console`; afterNavigate(({ from }) => { @@ -63,7 +53,7 @@ credits: null }; let taxId: string; - let budgetEnabled = false; + let billingBudget: number; let showCreditModal = false; @@ -190,15 +180,6 @@ $: if (billingPlan === BillingPlan.PRO) { loadPaymentMethods(); } - $: currentPlan = $plansInfo.get(billingPlan); - $: extraSeatsCost = (collaborators?.length ?? 0) * (currentPlan?.addons?.member?.price ?? 0); - $: grossCost = currentPlan.price + extraSeatsCost; - $: estimatedTotal = - couponData?.status === 'active' - ? grossCost - couponData.credits >= 0 - ? grossCost - couponData.credits - : 0 - : grossCost; @@ -293,97 +274,13 @@ {#if billingPlan !== BillingPlan.STARTER} - - -

    {currentPlan.name} plan

    -

    {formatCurrency(currentPlan.price)}

    -
    - -

    Additional seats ({collaborators?.length})

    -

    - {formatCurrency(extraSeatsCost)} -

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

    -

    - -
    - {#if couponData.credits > 100} -

    - Credits applied -

    - {:else} - -{formatCurrency(couponData.credits)} - {/if} -
    - {/if} -
    - -

    Estimated total

    -

    - {formatCurrency(estimatedTotal)} -

    -
    - - {@const trialEndDate = new Date( - billingPayDate.getTime() + currentPlan.trialDays * 24 * 60 * 60 * 1000 - )} -

    - Your payment method will be charged this amount plus usage fees every 30 - days {!currentPlan.trialDays - ? `starting ${toLocaleDate(billingPayDate.toString())}` - : ` after your trial period ends on ${toLocaleDate(trialEndDate.toString())}`}. -

    - - - {#if budgetEnabled} -
    - -
    - {/if} -
    -
    - + + {:else} + {/if} From 580d6541c2203d6242cb11a1457efd376c5929df Mon Sep 17 00:00:00 2001 From: Arman Date: Wed, 8 May 2024 15:04:43 +0200 Subject: [PATCH 11/30] chore: remove console log --- src/routes/console/create-organization/+page.svelte | 1 - 1 file changed, 1 deletion(-) diff --git a/src/routes/console/create-organization/+page.svelte b/src/routes/console/create-organization/+page.svelte index 29a1b8d58..1a3da3b72 100644 --- a/src/routes/console/create-organization/+page.svelte +++ b/src/routes/console/create-organization/+page.svelte @@ -36,7 +36,6 @@ afterNavigate(({ from }) => { previousPage = from?.url?.pathname || previousPage; - console.log(previousPage); }); let formComponent: Form; From 72a57fbc9cdd4d0494f8dc823d72865f950a8ee5 Mon Sep 17 00:00:00 2001 From: Arman Date: Wed, 8 May 2024 15:12:15 +0200 Subject: [PATCH 12/30] initial commit --- .../change-plan/+page.svelte | 283 ++++++++++++++++++ 1 file changed, 283 insertions(+) create mode 100644 src/routes/console/organization-[organization]/change-plan/+page.svelte diff --git a/src/routes/console/organization-[organization]/change-plan/+page.svelte b/src/routes/console/organization-[organization]/change-plan/+page.svelte new file mode 100644 index 000000000..18607f9c6 --- /dev/null +++ b/src/routes/console/organization-[organization]/change-plan/+page.svelte @@ -0,0 +1,283 @@ + + + + Change plan - Appwrite + + + + Change plan + +
    + +

    + For more details on our plans, visit our + . +

    +
      +
    • + + +
      +

      + {tierFree.name} + {#if $organization.billingPlan === BillingPlan.STARTER} + Current plan + {/if} +

      +

      {tierFree.description}

      +

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

      +
      +
      +
      +
    • + +
    • + + +
      +

      + {tierPro.name} + {#if $organization.billingPlan === BillingPlan.PRO} + Current plan + {/if} +

      +

      + {tierPro.description} +

      +

      + {formatCurrency(proPlan?.price ?? 0)} per member/month + usage +

      +
      +
      +
      +
    • +
    + {#if billingPlan === BillingPlan.PRO} + + + + + {#if !couponData?.code} + + {/if} + {/if} +
    + + {#if billingPlan !== BillingPlan.STARTER} + + {:else} + + {/if} + +
    + + + + + +
    + + From 9a305cb42e4ca52ff35c2c6f7398785d28606e09 Mon Sep 17 00:00:00 2001 From: Arman Date: Thu, 9 May 2024 16:04:58 +0200 Subject: [PATCH 13/30] fix: make address optional --- src/lib/sdk/billing.ts | 2 +- src/routes/console/create-organization/+page.svelte | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/sdk/billing.ts b/src/lib/sdk/billing.ts index 56980bc30..1036feda1 100644 --- a/src/lib/sdk/billing.ts +++ b/src/lib/sdk/billing.ts @@ -299,7 +299,7 @@ export class Billing { name: string, billingPlan: string, paymentMethodId: string, - billingAddressId: string + billingAddressId: string = undefined ): Promise { const path = `/organizations`; const params = { diff --git a/src/routes/console/create-organization/+page.svelte b/src/routes/console/create-organization/+page.svelte index 1a3da3b72..0eaba2d31 100644 --- a/src/routes/console/create-organization/+page.svelte +++ b/src/routes/console/create-organization/+page.svelte @@ -118,7 +118,7 @@ name, billingPlan, paymentMethodId, - '663a2ebf0ac1f0348a58' //TODO: change hardcoded address when it's not mandatory anymore + null ); //Add budget From ba74ffee95351281146643582fa555e7c8a14c6b Mon Sep 17 00:00:00 2001 From: Arman Date: Thu, 9 May 2024 22:18:38 +0200 Subject: [PATCH 14/30] feat: change tier functrion --- src/lib/sdk/billing.ts | 2 +- .../change-plan/+page.svelte | 178 +++++++++++++----- 2 files changed, 136 insertions(+), 44 deletions(-) diff --git a/src/lib/sdk/billing.ts b/src/lib/sdk/billing.ts index 1036feda1..a298eace4 100644 --- a/src/lib/sdk/billing.ts +++ b/src/lib/sdk/billing.ts @@ -356,7 +356,7 @@ export class Billing { organizationId: string, billingPlan: string, paymentMethodId: string, - billingAddressId: string + billingAddressId: string = undefined ): Promise { const path = `/organizations/${organizationId}/plan`; const params = { diff --git a/src/routes/console/organization-[organization]/change-plan/+page.svelte b/src/routes/console/organization-[organization]/change-plan/+page.svelte index 18607f9c6..e36ba90fe 100644 --- a/src/routes/console/organization-[organization]/change-plan/+page.svelte +++ b/src/routes/console/organization-[organization]/change-plan/+page.svelte @@ -24,7 +24,8 @@ import { addNotification } from '$lib/stores/notifications'; import { organization, organizationList, type Organization } from '$lib/stores/organization'; import { sdk } from '$lib/stores/sdk'; - import { ID } from '@appwrite.io/console'; + import { user } from '$lib/stores/user'; + import { VARS } from '$lib/system'; import { onMount } from 'svelte'; import { writable } from 'svelte/store'; @@ -87,31 +88,103 @@ paymentMethodId = methods.paymentMethods.find((method) => !!method?.last4)?.$id ?? null; } - async function create() { - try { - let org: Organization; + const feedbackDowngradeOptions = [ + { + value: 'availableFeatures', + label: "The available features don't meet my needs" + }, + { + value: 'traction', + label: "My project isn't getting traction" + }, + { + value: 'bugs', + label: 'I experienced bugs or unexpected outages while using the console' + }, + { + value: 'starter', + label: 'The Starter plan is enough for my projects' + }, + { + value: 'budget', + label: "I don't have the budget" + }, + { + value: 'tryOut', + label: 'I just wanted to try it out' + }, + { + value: 'alternative', + label: 'I found an alternative/competitor to meet my needs' + }, + { + value: 'other', + label: 'Other' + } + ]; - if (billingPlan === BillingPlan.STARTER) { - org = await sdk.forConsole.billing.createOrganization( - ID.unique(), - name, - BillingPlan.STARTER, - null, - null - ); - } else { - org = await sdk.forConsole.billing.createOrganization( - ID.unique(), - name, + let feedbackDowngradeReason: string; + let feedbackMessage: string; + + async function update() { + //Downgrade + if (billingPlan === BillingPlan.STARTER) { + try { + await sdk.forConsole.billing.updatePlan( + $organization.$id, billingPlan, paymentMethodId, - '663a2ebf0ac1f0348a58' //TODO: change hardcoded address when it's not mandatory anymore + null ); - //Add budget - if (billingBudget) { - await sdk.forConsole.billing.updateBudget(org.$id, billingBudget, [75]); - } + await fetch(`${VARS.GROWTH_ENDPOINT}/feedback/billing`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + from: tierToPlan($organization.billingPlan).name, + to: tierToPlan(billingPlan).name, + email: $user.email, + reason: feedbackDowngradeOptions.find( + (option) => option.value === feedbackDowngradeReason + )?.label, + orgId: $organization.$id, + userId: $user.$id, + message: feedbackMessage ?? '' + }) + }); + + addNotification({ + type: 'success', + isHtml: true, + message: ` + ${$organization.name} has been changed to ${ + tierToPlan(billingPlan).name + } plan.` + }); + + trackEvent(Submit.OrganizationDowngrade, { + plan: tierToPlan(billingPlan)?.name + }); + } catch (e) { + addNotification({ + type: 'error', + message: e.message + }); + trackError( + e, + isUpgrade ? Submit.OrganizationUpgrade : Submit.OrganizationDowngrade + ); + } + } else { + try { + const org = await sdk.forConsole.billing.updatePlan( + $organization.$id, + billingPlan, + paymentMethodId, + null + ); //Add coupon if (couponData?.code) { @@ -119,6 +192,11 @@ trackEvent(Submit.CreditRedeem); } + //Add budget + if (billingBudget) { + await sdk.forConsole.billing.updateBudget(org.$id, billingBudget, [75]); + } + //Add collaborators if (collaborators?.length) { collaborators.forEach(async (collaborator) => { @@ -133,34 +211,48 @@ }); } - // Add tax ID + //Add tax ID if (taxId) { await sdk.forConsole.billing.updateTaxId(org.$id, taxId); } + + await invalidate(Dependencies.ACCOUNT); + await invalidate(Dependencies.ORGANIZATION); + + await goto(`/console/organization-${org.$id}`); + if (isUpgrade) { + addNotification({ + type: 'success', + message: 'Your organization has been upgraded' + }); + } else { + addNotification({ + type: 'success', + isHtml: true, + message: ` + ${$organization.name} will change to ${ + tierToPlan(billingPlan).name + } plan at the end of the current billing cycle.` + }); + } + trackEvent(isUpgrade ? Submit.OrganizationUpgrade : Submit.OrganizationDowngrade, { + plan: tierToPlan(billingPlan)?.name + }); + } catch (e) { + addNotification({ + type: 'error', + message: e.message + }); + trackError( + e, + isUpgrade ? Submit.OrganizationUpgrade : Submit.OrganizationDowngrade + ); } - - trackEvent(Submit.OrganizationCreate, { - plan: tierToPlan(billingPlan)?.name, - budget_cap_enabled: !!billingBudget, - 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` - }); - } catch (e) { - addNotification({ - type: 'error', - message: e.message - }); - trackError(e, Submit.OrganizationCreate); } } + $: isUpgrade = billingPlan > $organization.billingPlan; + $: freePlan = $plansInfo.get(BillingPlan.STARTER); $: proPlan = $plansInfo.get(BillingPlan.PRO); $: if (billingPlan === BillingPlan.PRO) { @@ -175,7 +267,7 @@ Change plan -
    +

    For more details on our plans, visit our From 34103f9787cafb4ceaac2ebda3532f6a9b8d557e Mon Sep 17 00:00:00 2001 From: Arman Date: Mon, 13 May 2024 10:42:21 +0200 Subject: [PATCH 15/30] more fixes --- .../organization-[organization]/change-plan/+page.svelte | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/routes/console/organization-[organization]/change-plan/+page.svelte b/src/routes/console/organization-[organization]/change-plan/+page.svelte index e36ba90fe..1f1064969 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)}

    diff --git a/src/lib/components/billing/paymentModal.svelte b/src/lib/components/billing/paymentModal.svelte index 2b5ef5d1b..56bcdab20 100644 --- a/src/lib/components/billing/paymentModal.svelte +++ b/src/lib/components/billing/paymentModal.svelte @@ -68,7 +68,12 @@ } - + - + diff --git a/src/lib/components/billing/planComparisonBox.svelte b/src/lib/components/billing/planComparisonBox.svelte index d9ae9326e..85443bcfb 100644 --- a/src/lib/components/billing/planComparisonBox.svelte +++ b/src/lib/components/billing/planComparisonBox.svelte @@ -25,25 +25,25 @@
    {#if selectedTab === 'tier-0'}

    {plan.name} plan

    -
      -
    • +
        +
      • Limited to {plan.databases} Database, {plan.buckets} Buckets, {plan.functions} Functions per project
      • -
      • Limited to 1 organization member
      • -
      • {plan.bandwidth}GB bandwidth
      • -
      • {plan.storage}GB storage
      • -
      • {formatNum(plan.executions)} executions
      • +
      • Limited to 1 organization member
      • +
      • {plan.bandwidth}GB bandwidth
      • +
      • {plan.storage}GB storage
      • +
      • {formatNum(plan.executions)} executions
      {:else if selectedTab === 'tier-1'}

      {plan.name} plan

      -
        -
      • Everything in the Starter plan, plus:
      • -
      • Unlimited databases, buckets, functions
      • -
      • {plan.bandwidth}GB bandwidth
      • -
      • {plan.storage}GB storage
      • -
      • {formatNum(plan.executions)} executions
      • -
      • Email support
      • +
          +
        • Everything in the Starter plan, plus:
        • +
        • Unlimited databases, buckets, functions
        • +
        • {plan.bandwidth}GB bandwidth
        • +
        • {plan.storage}GB storage
        • +
        • {formatNum(plan.executions)} executions
        • +
        • Email support
        {/if}
    diff --git a/src/lib/components/billing/selectPaymentMethod.svelte b/src/lib/components/billing/selectPaymentMethod.svelte index 3e4d9269c..fd3ed238f 100644 --- a/src/lib/components/billing/selectPaymentMethod.svelte +++ b/src/lib/components/billing/selectPaymentMethod.svelte @@ -73,7 +73,7 @@ {:else} - +

    diff --git a/src/lib/constants.ts b/src/lib/constants.ts index fd4a80b82..3d994e792 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -350,3 +350,38 @@ export enum BillingPlan { PRO = 'tier-1', SCALE = 'tier-2' } + +export const feedbackDowngradeOptions = [ + { + value: 'availableFeatures', + label: "The available features don't meet my needs" + }, + { + value: 'traction', + label: "My project isn't getting traction" + }, + { + value: 'bugs', + label: 'I experienced bugs or unexpected outages while using the console' + }, + { + value: 'starter', + label: 'The Starter plan is enough for my projects' + }, + { + value: 'budget', + label: "I don't have the budget" + }, + { + value: 'tryOut', + label: 'I just wanted to try it out' + }, + { + value: 'alternative', + label: 'I found an alternative/competitor to meet my needs' + }, + { + value: 'other', + label: 'Other' + } +]; diff --git a/src/lib/layout/wizardSecondaryHeader.svelte b/src/lib/layout/wizardSecondaryHeader.svelte index e7971f533..91a5bda0e 100644 --- a/src/lib/layout/wizardSecondaryHeader.svelte +++ b/src/lib/layout/wizardSecondaryHeader.svelte @@ -5,8 +5,8 @@

    -
    - +
    + diff --git a/src/routes/console/organization-[organization]/change-plan/+page.svelte b/src/routes/console/organization-[organization]/change-plan/+page.svelte index 1f1064969..ca73b4ae6 100644 --- a/src/routes/console/organization-[organization]/change-plan/+page.svelte +++ b/src/routes/console/organization-[organization]/change-plan/+page.svelte @@ -3,15 +3,23 @@ import { base } from '$app/paths'; import { page } from '$app/stores'; import { Submit, trackError, trackEvent } from '$lib/actions/analytics'; - import { LabelCard } from '$lib/components'; + import { Alert, LabelCard } from '$lib/components'; import { EstimatedTotalBox, PlanComparisonBox, SelectPaymentMethod } from '$lib/components/billing'; import ValidateCreditModal from '$lib/components/billing/validateCreditModal.svelte'; - import { BillingPlan, Dependencies } from '$lib/constants'; - import { Button, Form, FormList, InputTags, Label } from '$lib/elements/forms'; + import { BillingPlan, Dependencies, feedbackDowngradeOptions } from '$lib/constants'; + import { + Button, + Form, + FormList, + InputSelect, + InputTags, + InputTextarea, + Label + } from '$lib/elements/forms'; import { formatCurrency } from '$lib/helpers/numbers'; import { WizardSecondaryContainer, @@ -19,16 +27,23 @@ WizardSecondaryFooter, WizardSecondaryHeader } from '$lib/layout'; - import type { Coupon, PaymentList } from '$lib/sdk/billing'; + import { type Coupon, type PaymentList } from '$lib/sdk/billing'; import { plansInfo, tierFree, tierPro, tierToPlan, type Tier } from '$lib/stores/billing'; import { addNotification } from '$lib/stores/notifications'; - import { organization, organizationList, type Organization } from '$lib/stores/organization'; + import { + organization, + organizationList, + type Organization, + type OrganizationList + } from '$lib/stores/organization'; import { sdk } from '$lib/stores/sdk'; import { user } from '$lib/stores/user'; import { VARS } from '$lib/system'; import { onMount } from 'svelte'; import { writable } from 'svelte/store'; + export let data; + $: anyOrgFree = $organizationList.teams?.find( (org) => (org as Organization)?.billingPlan === BillingPlan.STARTER ); @@ -41,21 +56,29 @@ let formComponent: Form; let isSubmitting = writable(false); - let methods: PaymentList; let billingPlan: Tier = $organization.billingPlan; let paymentMethodId: string; - let collaborators: string[] = []; + let collaborators: string[] = + data?.members?.memberships + ?.map((m) => { + if (m.userEmail !== $user.email) return m.userEmail; + }) + ?.filter(Boolean) ?? []; let couponData: Partial = { code: null, status: null, credits: null }; let taxId: string; - let billingBudget: number; let showCreditModal = false; + let feedbackDowngradeReason: string; + let feedbackMessage: string; + + let orgList: OrganizationList; + onMount(async () => { if ($page.url.searchParams.has('coupon')) { const coupon = $page.url.searchParams.get('coupon'); @@ -70,9 +93,6 @@ }; } } - if ($page.url.searchParams.has('name')) { - name = $page.url.searchParams.get('name'); - } if ($page.url.searchParams.has('plan')) { const plan = $page.url.searchParams.get('plan'); if (plan && plan in BillingPlan) { @@ -80,183 +100,163 @@ } } billingPlan = $organization.billingPlan; + + if (anyOrgFree && billingPlan === BillingPlan.PRO) { + orgList = await sdk.forConsole.billing.listOrganization(); + } }); async function loadPaymentMethods() { methods = await sdk.forConsole.billing.listPaymentMethods(); - paymentMethodId = methods.paymentMethods.find((method) => !!method?.last4)?.$id ?? null; + + paymentMethodId = + $organization?.paymentMethodId ?? + methods.paymentMethods.find((method) => !!method?.last4)?.$id ?? + null; } - const feedbackDowngradeOptions = [ - { - value: 'availableFeatures', - label: "The available features don't meet my needs" - }, - { - value: 'traction', - label: "My project isn't getting traction" - }, - { - value: 'bugs', - label: 'I experienced bugs or unexpected outages while using the console' - }, - { - value: 'starter', - label: 'The Starter plan is enough for my projects' - }, - { - value: 'budget', - label: "I don't have the budget" - }, - { - value: 'tryOut', - label: 'I just wanted to try it out' - }, - { - value: 'alternative', - label: 'I found an alternative/competitor to meet my needs' - }, - { - value: 'other', - label: 'Other' - } - ]; - - let feedbackDowngradeReason: string; - let feedbackMessage: string; - - async function update() { - //Downgrade + async function handleSubmit() { if (billingPlan === BillingPlan.STARTER) { - try { - await sdk.forConsole.billing.updatePlan( - $organization.$id, - billingPlan, - paymentMethodId, - null + await downgrade(); + } else { + await upgrade(); + } + } + + async function downgrade() { + try { + await sdk.forConsole.billing.updatePlan( + $organization.$id, + billingPlan, + paymentMethodId, + null + ); + + await fetch(`${VARS.GROWTH_ENDPOINT}/feedback/billing`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + from: tierToPlan($organization.billingPlan).name, + to: tierToPlan(billingPlan).name, + email: $user.email, + reason: feedbackDowngradeOptions.find( + (option) => option.value === feedbackDowngradeReason + )?.label, + orgId: $organization.$id, + userId: $user.$id, + message: feedbackMessage ?? '' + }) + }); + + addNotification({ + type: 'success', + isHtml: true, + message: ` + ${$organization.name} has been changed to ${ + tierToPlan(billingPlan).name + } plan.` + }); + + trackEvent(Submit.OrganizationDowngrade, { + plan: tierToPlan(billingPlan)?.name + }); + } catch (e) { + addNotification({ + type: 'error', + message: e.message + }); + trackError(e, isUpgrade ? Submit.OrganizationUpgrade : Submit.OrganizationDowngrade); + } + } + + async function upgrade() { + try { + const org = await sdk.forConsole.billing.updatePlan( + $organization.$id, + billingPlan, + paymentMethodId, + null + ); + + //Add coupon + if (couponData?.code) { + await sdk.forConsole.billing.addCredit(org.$id, couponData.code); + trackEvent(Submit.CreditRedeem); + } + + //Add budget + if (billingBudget) { + await sdk.forConsole.billing.updateBudget(org.$id, billingBudget, [75]); + } + + //Add collaborators + if (collaborators?.length) { + const newCollaborators = collaborators.filter( + (collaborator) => + !data?.members?.memberships?.find((m) => m.userEmail === collaborator) ); - - await fetch(`${VARS.GROWTH_ENDPOINT}/feedback/billing`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - from: tierToPlan($organization.billingPlan).name, - to: tierToPlan(billingPlan).name, - email: $user.email, - reason: feedbackDowngradeOptions.find( - (option) => option.value === feedbackDowngradeReason - )?.label, - orgId: $organization.$id, - userId: $user.$id, - message: feedbackMessage ?? '' - }) + newCollaborators.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); + } + + await invalidate(Dependencies.ACCOUNT); + await invalidate(Dependencies.ORGANIZATION); + + await goto(`/console/organization-${org.$id}`); + if (isUpgrade) { + addNotification({ + type: 'success', + message: 'Your organization has been upgraded' + }); + } else { addNotification({ type: 'success', isHtml: true, message: ` - ${$organization.name} has been changed to ${ - tierToPlan(billingPlan).name - } plan.` - }); - - trackEvent(Submit.OrganizationDowngrade, { - plan: tierToPlan(billingPlan)?.name - }); - } catch (e) { - addNotification({ - type: 'error', - message: e.message - }); - trackError( - e, - isUpgrade ? Submit.OrganizationUpgrade : Submit.OrganizationDowngrade - ); - } - } else { - try { - const org = await sdk.forConsole.billing.updatePlan( - $organization.$id, - billingPlan, - paymentMethodId, - null - ); - - //Add coupon - if (couponData?.code) { - await sdk.forConsole.billing.addCredit(org.$id, couponData.code); - trackEvent(Submit.CreditRedeem); - } - - //Add budget - if (billingBudget) { - await sdk.forConsole.billing.updateBudget(org.$id, billingBudget, [75]); - } - - //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); - } - - await invalidate(Dependencies.ACCOUNT); - await invalidate(Dependencies.ORGANIZATION); - - await goto(`/console/organization-${org.$id}`); - if (isUpgrade) { - addNotification({ - type: 'success', - message: 'Your organization has been upgraded' - }); - } else { - addNotification({ - type: 'success', - isHtml: true, - message: ` ${$organization.name} will change to ${ tierToPlan(billingPlan).name } plan at the end of the current billing cycle.` - }); - } - trackEvent(isUpgrade ? Submit.OrganizationUpgrade : Submit.OrganizationDowngrade, { - plan: tierToPlan(billingPlan)?.name }); - } catch (e) { - addNotification({ - type: 'error', - message: e.message - }); - trackError( - e, - isUpgrade ? Submit.OrganizationUpgrade : Submit.OrganizationDowngrade - ); } + trackEvent(isUpgrade ? Submit.OrganizationUpgrade : Submit.OrganizationDowngrade, { + plan: tierToPlan(billingPlan)?.name + }); + } catch (e) { + addNotification({ + type: 'error', + message: e.message + }); + trackError(e, isUpgrade ? Submit.OrganizationUpgrade : Submit.OrganizationDowngrade); } } $: isUpgrade = billingPlan > $organization.billingPlan; - $: freePlan = $plansInfo.get(BillingPlan.STARTER); $: proPlan = $plansInfo.get(BillingPlan.PRO); $: if (billingPlan === BillingPlan.PRO) { loadPaymentMethods(); } + $: isButtonDisabled = + $organization.billingPlan === billingPlan + ? true + : isUpgrade + ? !paymentMethodId + : !feedbackDowngradeReason; @@ -266,12 +266,20 @@ Change plan - +

    For more details on our plans, visit our .

    + {#if anyOrgFree && billingPlan === BillingPlan.PRO} + + You are limited to one Starter organization per account. Consider upgrading or + deleting . + + {/if}
      @@ -346,6 +354,22 @@ {/if} {/if} + {#if !isUpgrade && billingPlan === BillingPlan.STARTER} + + + + + {/if} {#if billingPlan !== BillingPlan.STARTER} @@ -365,7 +389,7 @@ diff --git a/src/routes/console/organization-[organization]/change-plan/+page.ts b/src/routes/console/organization-[organization]/change-plan/+page.ts new file mode 100644 index 000000000..cde580678 --- /dev/null +++ b/src/routes/console/organization-[organization]/change-plan/+page.ts @@ -0,0 +1,10 @@ +import { Dependencies } from '$lib/constants'; + +export const load = async ({ depends, parent }) => { + const { members } = await parent(); + depends(Dependencies.ORGANIZATION); + + return { + members + }; +}; From 6470cc014cd91e63b5f63824a5b15853cecd00dd Mon Sep 17 00:00:00 2001 From: Arman Date: Tue, 14 May 2024 11:40:06 +0200 Subject: [PATCH 18/30] fix: error --- .../change-plan/+page.svelte | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/routes/console/organization-[organization]/change-plan/+page.svelte b/src/routes/console/organization-[organization]/change-plan/+page.svelte index ca73b4ae6..9ff13282a 100644 --- a/src/routes/console/organization-[organization]/change-plan/+page.svelte +++ b/src/routes/console/organization-[organization]/change-plan/+page.svelte @@ -30,12 +30,7 @@ import { type Coupon, type PaymentList } from '$lib/sdk/billing'; import { plansInfo, tierFree, tierPro, tierToPlan, type Tier } from '$lib/stores/billing'; import { addNotification } from '$lib/stores/notifications'; - import { - organization, - organizationList, - type Organization, - type OrganizationList - } from '$lib/stores/organization'; + import { organization, organizationList, type Organization } from '$lib/stores/organization'; import { sdk } from '$lib/stores/sdk'; import { user } from '$lib/stores/user'; import { VARS } from '$lib/system'; @@ -77,8 +72,6 @@ let feedbackDowngradeReason: string; let feedbackMessage: string; - let orgList: OrganizationList; - onMount(async () => { if ($page.url.searchParams.has('coupon')) { const coupon = $page.url.searchParams.get('coupon'); @@ -100,10 +93,6 @@ } } billingPlan = $organization.billingPlan; - - if (anyOrgFree && billingPlan === BillingPlan.PRO) { - orgList = await sdk.forConsole.billing.listOrganization(); - } }); async function loadPaymentMethods() { From e130543aa5f7a668ada0a38caedae0d321ea9baf Mon Sep 17 00:00:00 2001 From: Arman Date: Tue, 14 May 2024 11:44:21 +0200 Subject: [PATCH 19/30] fix: hide fields when current plan selected --- .../organization-[organization]/change-plan/+page.svelte | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/routes/console/organization-[organization]/change-plan/+page.svelte b/src/routes/console/organization-[organization]/change-plan/+page.svelte index 9ff13282a..c14595e39 100644 --- a/src/routes/console/organization-[organization]/change-plan/+page.svelte +++ b/src/routes/console/organization-[organization]/change-plan/+page.svelte @@ -320,7 +320,7 @@
    - {#if billingPlan === BillingPlan.PRO} + {#if billingPlan === BillingPlan.PRO && $organization.billingPlan !== BillingPlan.PRO} {/if} {/if} - {#if !isUpgrade && billingPlan === BillingPlan.STARTER} + {#if !isUpgrade && billingPlan === BillingPlan.STARTER && $organization.billingPlan !== BillingPlan.STARTER} - {#if billingPlan !== BillingPlan.STARTER} + {#if billingPlan !== BillingPlan.STARTER && $organization.billingPlan !== BillingPlan.PRO} Date: Tue, 14 May 2024 15:01:37 +0200 Subject: [PATCH 20/30] feat: add missing alerts --- .../components/billing/selectPaymentMethod.svelte | 13 ++++++++++++- .../change-plan/+page.svelte | 7 +++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/lib/components/billing/selectPaymentMethod.svelte b/src/lib/components/billing/selectPaymentMethod.svelte index fd3ed238f..1e2db6a94 100644 --- a/src/lib/components/billing/selectPaymentMethod.svelte +++ b/src/lib/components/billing/selectPaymentMethod.svelte @@ -4,7 +4,7 @@ import { sdk } from '$lib/stores/sdk'; import { hasStripePublicKey, isCloud } from '$lib/system'; import { onMount } from 'svelte'; - import { Card, CreditCardBrandImage } from '..'; + import { Alert, Card, CreditCardBrandImage } from '..'; import PaymentModal from './paymentModal.svelte'; import { capitalize } from '$lib/helpers/string'; @@ -27,9 +27,20 @@ }); $: filteredMethods = methods?.paymentMethods?.filter((method) => !!method?.last4); + + $: selectedPaymentMethod = methods?.paymentMethods?.find((method) => method.$id === value); {#if filteredMethods?.length} + {#if selectedPaymentMethod?.country === 'in'} + + Indian credit or debit card-holders + To comply with RBI regulations in India, Appwrite will ask for verification to charge up + to $150 USD on your payment method. We will never charge more than the cost of your plan + and the resources you use, or your budget cap limit. For higher usage limits, please contact + us. + + {/if} pricing page.

    {#if anyOrgFree && billingPlan === BillingPlan.PRO} - + You are limited to one Starter organization per account. Consider upgrading or deleting - +
    {#if !couponData?.code}
    diff --git a/src/lib/components/support.svelte b/src/lib/components/support.svelte index d6a8f0ef9..ce925487e 100644 --- a/src/lib/components/support.svelte +++ b/src/lib/components/support.svelte @@ -7,9 +7,9 @@ import { isCloud } from '$lib/system'; import { organization } from '$lib/stores/organization'; import { BillingPlan } from '$lib/constants'; - import ChangeOrganizationTierCloud from '$routes/console/changeOrganizationTierCloud.svelte'; import { trackEvent } from '$lib/actions/analytics'; import { localeTimezoneName, utcHourToLocaleHour } from '$lib/helpers/date'; + import { base } from '$app/paths'; export let show = false; @@ -33,9 +33,8 @@ {#if $organization?.billingPlan === BillingPlan.STARTER} +
    diff --git a/src/lib/components/support.svelte b/src/lib/components/support.svelte index ce925487e..ec768ed4a 100644 --- a/src/lib/components/support.svelte +++ b/src/lib/components/support.svelte @@ -9,7 +9,7 @@ import { BillingPlan } from '$lib/constants'; import { trackEvent } from '$lib/actions/analytics'; import { localeTimezoneName, utcHourToLocaleHour } from '$lib/helpers/date'; - import { base } from '$app/paths'; + import { upgradeURL } from '$lib/stores/billing'; export let show = false; @@ -33,7 +33,7 @@ {#if $organization?.billingPlan === BillingPlan.STARTER} - - - -
    - - {#if $changeOrganizationTier?.collaborators?.length} -
    - - - Collaborator - {#if $changeOrganizationTier.billingPlan === BillingPlan.PRO} - Cost - {/if} - - - - {#each $changeOrganizationTier.collaborators as collaborator} - - {collaborator} - {#if $changeOrganizationTier.billingPlan === BillingPlan.PRO} - {formatCurrency(15)} - {/if} - - - - - {/each} - -
    -
    - {/if} - diff --git a/src/routes/console/wizard/cloudOrganizationChangeTier/paymentDetails.svelte b/src/routes/console/wizard/cloudOrganizationChangeTier/paymentDetails.svelte deleted file mode 100644 index 99116f394..000000000 --- a/src/routes/console/wizard/cloudOrganizationChangeTier/paymentDetails.svelte +++ /dev/null @@ -1,119 +0,0 @@ - - - - Payment details - - Confirm the payment method for your organization. - - - - - - -

    - Restrict your resource usage by setting a budget cap. . -

    - {#if budgetEnabled} -
    - -
    - {/if} -
    -
    -
    diff --git a/src/routes/console/wizard/cloudOrganizationChangeTier/store.ts b/src/routes/console/wizard/cloudOrganizationChangeTier/store.ts deleted file mode 100644 index 57463d106..000000000 --- a/src/routes/console/wizard/cloudOrganizationChangeTier/store.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { BillingPlan } from '$lib/constants'; -import type { WizardStepsType } from '$lib/layout/wizard.svelte'; -import { type Tier } from '$lib/stores/billing'; -import { writable } from 'svelte/store'; - -export const changeTierSteps = writable(new Map()); -export const isUpgrade = writable(false); -export const changeOrganizationFinalAction = writable('Create'); - -export const changeOrganizationTier = writable<{ - billingPlan: Tier; - paymentMethodId: string; - billingAddressId: string; - billingBudget?: number; - collaborators?: string[]; - isOverLimit?: boolean; - limitOverflow?: { - bandwidth?: number; - executions?: number; - storage?: number; - users?: number; - members?: number; - }; - taxId?: string; - feedbackMessage?: string; - feedbackDowngradeReason?: string; - couponCode?: string; -}>({ - billingPlan: BillingPlan.PRO, - paymentMethodId: null, - collaborators: [], - isOverLimit: false, - billingAddressId: null, - taxId: null -}); - -export const feedbackDowngradeOptions = [ - { - value: 'availableFeatures', - label: "The available features don't meet my needs" - }, - { - value: 'traction', - label: "My project isn't getting traction" - }, - { - value: 'bugs', - label: 'I experienced bugs or unexpected outages while using the console' - }, - { - value: 'starter', - label: 'The Starter plan is enough for my projects' - }, - { - value: 'budget', - label: "I don't have the budget" - }, - { - value: 'tryOut', - label: 'I just wanted to try it out' - }, - { - value: 'alternative', - label: 'I found an alternative/competitor to meet my needs' - }, - { - value: 'other', - label: 'Other' - } -]; diff --git a/src/routes/console/wizard/cloudOrganizationChangeTier/usageExcess.svelte b/src/routes/console/wizard/cloudOrganizationChangeTier/usageExcess.svelte deleted file mode 100644 index e5bd500fb..000000000 --- a/src/routes/console/wizard/cloudOrganizationChangeTier/usageExcess.svelte +++ /dev/null @@ -1,20 +0,0 @@ - - - - - Your usage exceeds the limits of the {tierToPlan($organization.billingPlan).name} plan - - View the current usage for {$organization.name} and where you will exceed the limits of - your new plan. - - - - From fbfab72a207f4a71d171a2b8251c7f3bcfcf9ef3 Mon Sep 17 00:00:00 2001 From: Arman Date: Wed, 15 May 2024 16:01:12 +0200 Subject: [PATCH 25/30] chore: finish removing old wizard --- .../changeOrganizationTierCloud.svelte | 221 ------------------ 1 file changed, 221 deletions(-) delete mode 100644 src/routes/console/changeOrganizationTierCloud.svelte diff --git a/src/routes/console/changeOrganizationTierCloud.svelte b/src/routes/console/changeOrganizationTierCloud.svelte deleted file mode 100644 index 2e2ad4544..000000000 --- a/src/routes/console/changeOrganizationTierCloud.svelte +++ /dev/null @@ -1,221 +0,0 @@ - - - From fcc8fde2cfe64b327d4e9c5d07b578130f27a8ff Mon Sep 17 00:00:00 2001 From: Arman Date: Tue, 21 May 2024 11:00:00 +0200 Subject: [PATCH 26/30] fix: code review --- src/lib/components/billing/selectPaymentMethod.svelte | 2 +- src/lib/elements/forms/label.svelte | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lib/components/billing/selectPaymentMethod.svelte b/src/lib/components/billing/selectPaymentMethod.svelte index 1e2db6a94..ec1e37570 100644 --- a/src/lib/components/billing/selectPaymentMethod.svelte +++ b/src/lib/components/billing/selectPaymentMethod.svelte @@ -84,7 +84,7 @@ {:else} - +

    diff --git a/src/lib/elements/forms/label.svelte b/src/lib/elements/forms/label.svelte index 5601770b6..0bb328f65 100644 --- a/src/lib/elements/forms/label.svelte +++ b/src/lib/elements/forms/label.svelte @@ -16,14 +16,12 @@ export let optionalText: $$Props['optionalText'] = undefined; export let hide: $$Props['hide'] = false; export let tooltip: $$Props['tooltip'] = null; - let classes: string = undefined; - export { classes as class }; From 4f3ec5c5dbccb1aa24408bad3fce8913ee40ef91 Mon Sep 17 00:00:00 2001 From: Arman Date: Wed, 22 May 2024 17:01:42 +0200 Subject: [PATCH 27/30] fix: design and bugs --- package-lock.json | 18 +++++----- package.json | 4 +-- .../billing/alerts/limitReached.svelte | 4 +-- .../billing/alerts/markedForDeletion.svelte | 3 +- .../alerts/missingPaymentMethod.svelte | 3 +- .../billing/alerts/paymentAuthRequired.svelte | 4 +-- .../billing/alerts/paymentMandate.svelte | 4 +-- .../billing/alerts/projectsAtRisk.svelte | 4 +-- .../billing/estimatedTotalBox.svelte | 13 ++++--- .../components/billing/paymentModal.svelte | 1 + .../billing/selectPaymentMethod.svelte | 35 ++++++++++++------- src/lib/components/card.svelte | 1 + .../layout/wizardSecondaryContainer.svelte | 2 +- src/lib/stores/billing.ts | 2 ++ .../console/create-organization/+page.svelte | 2 +- .../change-plan/+page.svelte | 2 +- 16 files changed, 58 insertions(+), 44 deletions(-) diff --git a/package-lock.json b/package-lock.json index fe2801929..83d39ffc4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,8 +7,8 @@ "name": "@appwrite/console", "dependencies": { "@appwrite.io/console": "^0.6.1", - "@appwrite.io/pink": "0.17.0", - "@appwrite.io/pink-icons": "0.17.0", + "@appwrite.io/pink": "0.20.0", + "@appwrite.io/pink-icons": "0.20.0", "@popperjs/core": "^2.11.8", "@sentry/svelte": "^7.66.0", "@sentry/tracing": "^7.66.0", @@ -166,19 +166,19 @@ } }, "node_modules/@appwrite.io/pink": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@appwrite.io/pink/-/pink-0.17.0.tgz", - "integrity": "sha512-1FkVVqdqU5d0FlGIyLisCoLR3JaqGlQs3+BJydo6Mr9/bRWKcg4O9nw+e1P1Lx8StnVnDEM5CRCQdzyPQSNByw==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@appwrite.io/pink/-/pink-0.20.0.tgz", + "integrity": "sha512-17anov7EL+Ic/4fNtCYgG3wA+hoeBXn/j+ZjkfZFfX3g66M8XzVTaQfsAH+EMZ3YjvWuKlHtn+bY4BkGwvMIzQ==", "dependencies": { - "@appwrite.io/pink-icons": "0.17.0", + "@appwrite.io/pink-icons": "0.20.0", "normalize.css": "^8.0.1", "the-new-css-reset": "^1.11.2" } }, "node_modules/@appwrite.io/pink-icons": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@appwrite.io/pink-icons/-/pink-icons-0.17.0.tgz", - "integrity": "sha512-JcYjVI+nPO8BTJzKlUH2RKyUYg/3pEy81dr3TrQB0HlsrngDq3akhaNCfnQzLg9Kicdl+Cr4Gq920Ha6zA4FQQ==" + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@appwrite.io/pink-icons/-/pink-icons-0.20.0.tgz", + "integrity": "sha512-nfDy7qE6FQWVO1eC+3nwGjXkj/Zm2V50rhvyj0/HKq8RyWNWFGX31BZoMSim01vl/N5irBilpkaER6mLPp7oEw==" }, "node_modules/@babel/code-frame": { "version": "7.24.2", diff --git a/package.json b/package.json index 1ebbf9ccb..866390627 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,8 @@ "e2e": "playwright test tests/e2e" }, "dependencies": { - "@appwrite.io/pink": "0.17.0", - "@appwrite.io/pink-icons": "0.17.0", + "@appwrite.io/pink": "0.20.0", + "@appwrite.io/pink-icons": "0.20.0", "@appwrite.io/console": "^0.6.1", "@popperjs/core": "^2.11.8", "@sentry/svelte": "^7.66.0", diff --git a/src/lib/components/billing/alerts/limitReached.svelte b/src/lib/components/billing/alerts/limitReached.svelte index 7666658e4..7cb72e1d0 100644 --- a/src/lib/components/billing/alerts/limitReached.svelte +++ b/src/lib/components/billing/alerts/limitReached.svelte @@ -5,11 +5,11 @@ import { BillingPlan } from '$lib/constants'; import { Button } from '$lib/elements/forms'; import { HeaderAlert } from '$lib/layout'; - import { readOnly, tierToPlan, upgradeURL } from '$lib/stores/billing'; + import { hideBillingHeaderRoutes, readOnly, tierToPlan, upgradeURL } from '$lib/stores/billing'; import { organization } from '$lib/stores/organization'; -{#if $organization?.$id && $organization?.billingPlan === BillingPlan.STARTER && $readOnly && !$page.url.pathname.includes('/console/account')} +{#if $organization?.$id && $organization?.billingPlan === BillingPlan.STARTER && $readOnly && !hideBillingHeaderRoutes.includes($page.url.pathname)} diff --git a/src/lib/components/billing/alerts/markedForDeletion.svelte b/src/lib/components/billing/alerts/markedForDeletion.svelte index e6323850b..6ff3cd426 100644 --- a/src/lib/components/billing/alerts/markedForDeletion.svelte +++ b/src/lib/components/billing/alerts/markedForDeletion.svelte @@ -1,10 +1,11 @@ -{#if $organization?.markedForDeletion && !$page.url.pathname.includes('/console/account')} +{#if $organization?.markedForDeletion && !hideBillingHeaderRoutes.includes($page.url.pathname)} All existing projects in the {$organization.name} organization have been paused. This organization diff --git a/src/lib/components/billing/alerts/missingPaymentMethod.svelte b/src/lib/components/billing/alerts/missingPaymentMethod.svelte index b39fd7508..b1bfe7f1b 100644 --- a/src/lib/components/billing/alerts/missingPaymentMethod.svelte +++ b/src/lib/components/billing/alerts/missingPaymentMethod.svelte @@ -3,10 +3,11 @@ import { BillingPlan } from '$lib/constants'; import { Button } from '$lib/elements/forms'; import { HeaderAlert } from '$lib/layout'; + import { hideBillingHeaderRoutes } from '$lib/stores/billing'; import { orgMissingPaymentMethod } from '$routes/console/store'; -{#if ($orgMissingPaymentMethod.billingPlan === BillingPlan.PRO || $orgMissingPaymentMethod.billingPlan === BillingPlan.SCALE) && !$orgMissingPaymentMethod.paymentMethodId && !$orgMissingPaymentMethod.backupPaymentMethodId && !$page.url.pathname.includes('/console/account')} +{#if ($orgMissingPaymentMethod.billingPlan === BillingPlan.PRO || $orgMissingPaymentMethod.billingPlan === BillingPlan.SCALE) && !$orgMissingPaymentMethod.paymentMethodId && !$orgMissingPaymentMethod.backupPaymentMethodId && !hideBillingHeaderRoutes.includes($page.url.pathname)} diff --git a/src/lib/components/billing/alerts/paymentAuthRequired.svelte b/src/lib/components/billing/alerts/paymentAuthRequired.svelte index d8819b492..d1491bf61 100644 --- a/src/lib/components/billing/alerts/paymentAuthRequired.svelte +++ b/src/lib/components/billing/alerts/paymentAuthRequired.svelte @@ -2,14 +2,14 @@ import { page } from '$app/stores'; import { Button } from '$lib/elements/forms'; import { HeaderAlert } from '$lib/layout'; - import { actionRequiredInvoices } from '$lib/stores/billing'; + import { actionRequiredInvoices, hideBillingHeaderRoutes } from '$lib/stores/billing'; import { organization } from '$lib/stores/organization'; import { VARS } from '$lib/system'; const endpoint = VARS.APPWRITE_ENDPOINT ?? `${$page.url.origin}/v1`; -{#if $actionRequiredInvoices && $actionRequiredInvoices?.invoices?.length && !$page.url.pathname.includes('/console/account')} +{#if $actionRequiredInvoices && $actionRequiredInvoices?.invoices?.length && !hideBillingHeaderRoutes.includes($page.url.pathname)} Please authorize your upcoming payment for {$organization.name}. Your bank requires this security measure to proceed with payment. diff --git a/src/lib/components/billing/alerts/paymentMandate.svelte b/src/lib/components/billing/alerts/paymentMandate.svelte index 9799ddcc3..8a7c7356a 100644 --- a/src/lib/components/billing/alerts/paymentMandate.svelte +++ b/src/lib/components/billing/alerts/paymentMandate.svelte @@ -2,7 +2,7 @@ import { page } from '$app/stores'; import { Button } from '$lib/elements/forms'; import { HeaderAlert } from '$lib/layout'; - import { paymentMissingMandate } from '$lib/stores/billing'; + import { hideBillingHeaderRoutes, paymentMissingMandate } from '$lib/stores/billing'; import { organization } from '$lib/stores/organization'; import { sdk } from '$lib/stores/sdk'; import { confirmSetup } from '$lib/stores/stripe'; @@ -16,7 +16,7 @@ } -{#if $paymentMissingMandate && $paymentMissingMandate.country === 'in' && $paymentMissingMandate.mandateId === null && !$page.url.pathname.includes('/console/account')} +{#if $paymentMissingMandate && $paymentMissingMandate.country === 'in' && $paymentMissingMandate.mandateId === null && !hideBillingHeaderRoutes.includes($page.url.pathname)} The payment method for {$organization.name} needs to be verified. diff --git a/src/lib/components/billing/alerts/projectsAtRisk.svelte b/src/lib/components/billing/alerts/projectsAtRisk.svelte index 348fe3ebd..a6c656a95 100644 --- a/src/lib/components/billing/alerts/projectsAtRisk.svelte +++ b/src/lib/components/billing/alerts/projectsAtRisk.svelte @@ -4,10 +4,10 @@ import { Button } from '$lib/elements/forms'; import { diffDays, toLocaleDate } from '$lib/helpers/date'; import { HeaderAlert } from '$lib/layout'; - import { failedInvoice } from '$lib/stores/billing'; + import { failedInvoice, hideBillingHeaderRoutes } from '$lib/stores/billing'; -{#if $failedInvoice && !$page.url.pathname.includes('/console/account')} +{#if $failedInvoice && !hideBillingHeaderRoutes.includes($page.url.pathname)} {@const daysPassed = diffDays(new Date($failedInvoice.dueAt), new Date())} diff --git a/src/lib/components/billing/estimatedTotalBox.svelte b/src/lib/components/billing/estimatedTotalBox.svelte index 6ead40362..932700919 100644 --- a/src/lib/components/billing/estimatedTotalBox.svelte +++ b/src/lib/components/billing/estimatedTotalBox.svelte @@ -5,7 +5,6 @@ import { formatCurrency } from '$lib/helpers/numbers'; import type { Coupon } from '$lib/sdk/billing'; import { plansInfo, type Tier } from '$lib/stores/billing'; - import { Box } from '..'; export let billingPlan: Tier; export let collaborators: string[]; @@ -26,9 +25,12 @@ ? grossCost - couponData.credits : 0 : grossCost; + $: trialEndDate = new Date( + billingPayDate.getTime() + currentPlan.trialDays * 24 * 60 * 60 * 1000 + ); - +

    {currentPlan.name} plan

    {formatCurrency(currentPlan.price)}

    @@ -83,15 +85,12 @@

    - {@const trialEndDate = new Date( - billingPayDate.getTime() + currentPlan.trialDays * 24 * 60 * 60 * 1000 - )}

    Your payment method will be charged this amount plus usage fees every 30 days {!currentPlan.trialDays ? `starting ${toLocaleDate(billingPayDate.toString())}` : ` after your trial period ends on ${toLocaleDate(trialEndDate.toString())}`}.

    - + - +
    diff --git a/src/lib/components/billing/paymentModal.svelte b/src/lib/components/billing/paymentModal.svelte index 56bcdab20..16388b948 100644 --- a/src/lib/components/billing/paymentModal.svelte +++ b/src/lib/components/billing/paymentModal.svelte @@ -92,6 +92,7 @@
    + diff --git a/src/lib/components/billing/selectPaymentMethod.svelte b/src/lib/components/billing/selectPaymentMethod.svelte index ec1e37570..873814887 100644 --- a/src/lib/components/billing/selectPaymentMethod.svelte +++ b/src/lib/components/billing/selectPaymentMethod.svelte @@ -84,7 +84,10 @@ {:else} - +

    @@ -99,17 +102,23 @@ {#if showPaymentModal && isCloud && hasStripePublicKey} - - {#if showTaxId} -

    - -
    - {/if} - + + + {#if showTaxId} +
    + +
    + {/if} +
    +
    {/if} diff --git a/src/lib/components/card.svelte b/src/lib/components/card.svelte index 378aea133..4cf714226 100644 --- a/src/lib/components/card.svelte +++ b/src/lib/components/card.svelte @@ -49,6 +49,7 @@ class:is-border-dashed={isDashed} class:is-danger={danger} class:is-allowed-focus={href} + {...$$restProps} {style} on:click on:keyup={clickOnEnter} diff --git a/src/lib/layout/wizardSecondaryContainer.svelte b/src/lib/layout/wizardSecondaryContainer.svelte index 7952cbbe8..976e849e8 100644 --- a/src/lib/layout/wizardSecondaryContainer.svelte +++ b/src/lib/layout/wizardSecondaryContainer.svelte @@ -6,7 +6,6 @@ diff --git a/src/lib/stores/billing.ts b/src/lib/stores/billing.ts index 1a1507675..bd6b2881b 100644 --- a/src/lib/stores/billing.ts +++ b/src/lib/stores/billing.ts @@ -367,3 +367,5 @@ export const upgradeURL = derived( page, ($page) => `${base}/console/organization-${$page.data?.organization?.$id}/change-plan` ); + +export const hideBillingHeaderRoutes = ['/console/create-organization', '/console/account']; diff --git a/src/routes/console/create-organization/+page.svelte b/src/routes/console/create-organization/+page.svelte index 0eaba2d31..561cfb0ea 100644 --- a/src/routes/console/create-organization/+page.svelte +++ b/src/routes/console/create-organization/+page.svelte @@ -197,7 +197,7 @@ id="name" required /> - +

    For more details on our plans, visit our . diff --git a/src/routes/console/organization-[organization]/change-plan/+page.svelte b/src/routes/console/organization-[organization]/change-plan/+page.svelte index 92bbc10cb..0986933be 100644 --- a/src/routes/console/organization-[organization]/change-plan/+page.svelte +++ b/src/routes/console/organization-[organization]/change-plan/+page.svelte @@ -256,7 +256,7 @@ Change plan

    - +

    For more details on our plans, visit our . From 021db90bcf1d5f64bd19811d647b7fc60fdc92d4 Mon Sep 17 00:00:00 2001 From: Arman Date: Wed, 22 May 2024 17:12:09 +0200 Subject: [PATCH 28/30] refactor: alert buttons --- src/lib/components/alert.svelte | 18 ++++++++++-------- .../(migration-wizard)/resource-form.svelte | 8 ++++---- .../auth/templates/+page.svelte | 12 ++++-------- .../settings/migrations/details.svelte | 4 ++-- 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/lib/components/alert.svelte b/src/lib/components/alert.svelte index 20998db9d..f37e83fe1 100644 --- a/src/lib/components/alert.svelte +++ b/src/lib/components/alert.svelte @@ -1,11 +1,13 @@