diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index ab33a8f375..91c98b81ff 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -42,6 +42,7 @@ jobs:
"PUBLIC_GROWTH_ENDPOINT=${{ secrets.PUBLIC_GROWTH_ENDPOINT }}"
"PUBLIC_STRIPE_KEY=${{ secrets.PUBLIC_STRIPE_KEY }}"
"SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}"
+ "SENTRY_RELEASE=${{ github.event.release.tag_name }}"
publish-cloud-stage:
runs-on: ubuntu-latest
steps:
diff --git a/Dockerfile b/Dockerfile
index 0e9e98f7e6..352e69e373 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -6,6 +6,7 @@ ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN npm i -g corepack@latest
RUN corepack enable
+RUN corepack prepare pnpm@10.0.0 --activate
ADD ./package.json /app/package.json
ADD ./pnpm-lock.yaml /app/pnpm-lock.yaml
@@ -24,12 +25,14 @@ ARG PUBLIC_APPWRITE_ENDPOINT
ARG PUBLIC_GROWTH_ENDPOINT
ARG PUBLIC_STRIPE_KEY
ARG SENTRY_AUTH_TOKEN
+ARG SENTRY_RELEASE
ENV PUBLIC_APPWRITE_ENDPOINT=$PUBLIC_APPWRITE_ENDPOINT
ENV PUBLIC_GROWTH_ENDPOINT=$PUBLIC_GROWTH_ENDPOINT
ENV PUBLIC_CONSOLE_MODE=$PUBLIC_CONSOLE_MODE
ENV PUBLIC_STRIPE_KEY=$PUBLIC_STRIPE_KEY
ENV SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN
+ENV SENTRY_RELEASE=$SENTRY_RELEASE
ENV NODE_OPTIONS=--max_old_space_size=8192
RUN pnpm run sync && pnpm run build
@@ -39,4 +42,4 @@ FROM nginx:1.25-alpine
EXPOSE 80
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
-COPY --from=build /app/build /usr/share/nginx/html/console
\ No newline at end of file
+COPY --from=build /app/build /usr/share/nginx/html/console
diff --git a/package.json b/package.json
index b6069da58f..5244491c71 100644
--- a/package.json
+++ b/package.json
@@ -25,7 +25,7 @@
"@popperjs/core": "^2.11.8",
"@sentry/sveltekit": "^8.38.0",
"@stripe/stripe-js": "^3.5.0",
- "@ai-sdk/svelte": "^1.1.22",
+ "@ai-sdk/svelte": "^1.1.24",
"analytics": "^0.8.14",
"cron-parser": "^4.9.0",
"dayjs": "^1.11.13",
diff --git a/playwright.config.ts b/playwright.config.ts
index 3f39d87611..fe964db382 100644
--- a/playwright.config.ts
+++ b/playwright.config.ts
@@ -12,7 +12,7 @@ const config: PlaywrightTestConfig = {
webServer: {
timeout: 120000,
env: {
- PUBLIC_APPWRITE_ENDPOINT: 'https://console-testing-2.appwrite.org/v1',
+ PUBLIC_APPWRITE_ENDPOINT: 'https://dlbillingic.appwrite.org/v1',
PUBLIC_CONSOLE_MODE: 'cloud',
PUBLIC_STRIPE_KEY:
'pk_test_51LT5nsGYD1ySxNCyd7b304wPD8Y1XKKWR6hqo6cu3GIRwgvcVNzoZv4vKt5DfYXL1gRGw4JOqE19afwkJYJq1g3K004eVfpdWn'
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ead7df1511..d26f402acc 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -9,7 +9,7 @@ importers:
.:
dependencies:
'@ai-sdk/svelte':
- specifier: ^1.1.22
+ specifier: ^1.1.24
version: 1.1.24(svelte@4.2.19)(zod@3.24.2)
'@appwrite.io/console':
specifier: 1.5.2
@@ -2712,13 +2712,13 @@ packages:
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
- nanoid@3.3.10:
- resolution: {integrity: sha512-vSJJTG+t/dIKAUhUDw/dLdZ9s//5OxcHqLaDWWrW4Cdq7o6tdLIczUkMXt2MBNmk6sJRZBZRXVixs7URY1CmIg==}
+ nanoid@3.3.7:
+ resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
- nanoid@3.3.7:
- resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
+ nanoid@3.3.9:
+ resolution: {integrity: sha512-SppoicMGpZvbF1l3z4x7No3OlIjP7QJvC9XR7AhZr1kL133KHnKPztkKDc+Ir4aJ/1VhTySrtKhrsycmrMQfvg==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
@@ -3712,8 +3712,8 @@ packages:
resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==}
engines: {node: '>=12.20'}
- zod-to-json-schema@3.24.4:
- resolution: {integrity: sha512-0uNlcvgabyrni9Ag8Vghj21drk7+7tp7VTwwR7KxxXXc/3pbXz2PHlDgj3cICahgF1kHm4dExBFj7BXrZJXzig==}
+ zod-to-json-schema@3.24.3:
+ resolution: {integrity: sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A==}
peerDependencies:
zod: ^3.24.1
@@ -3731,7 +3731,7 @@ snapshots:
dependencies:
'@ai-sdk/provider': 1.0.11
eventsource-parser: 3.0.0
- nanoid: 3.3.10
+ nanoid: 3.3.9
secure-json-parse: 2.7.0
optionalDependencies:
zod: 3.24.2
@@ -3753,7 +3753,7 @@ snapshots:
dependencies:
'@ai-sdk/provider': 1.0.11
'@ai-sdk/provider-utils': 2.1.13(zod@3.24.2)
- zod-to-json-schema: 3.24.4(zod@3.24.2)
+ zod-to-json-schema: 3.24.3(zod@3.24.2)
optionalDependencies:
zod: 3.24.2
@@ -6769,10 +6769,10 @@ snapshots:
ms@2.1.3: {}
- nanoid@3.3.10: {}
-
nanoid@3.3.7: {}
+ nanoid@3.3.9: {}
+
nanoid@5.0.8: {}
natural-compare@1.4.0: {}
@@ -7672,7 +7672,7 @@ snapshots:
yocto-queue@1.1.1: {}
- zod-to-json-schema@3.24.4(zod@3.24.2):
+ zod-to-json-schema@3.24.3(zod@3.24.2):
dependencies:
zod: 3.24.2
diff --git a/src/lib/actions/analytics.ts b/src/lib/actions/analytics.ts
index 036cf56c89..260cffe519 100644
--- a/src/lib/actions/analytics.ts
+++ b/src/lib/actions/analytics.ts
@@ -6,6 +6,7 @@ import { user } from '$lib/stores/user';
import { ENV, MODE, VARS, isCloud } from '$lib/system';
import { AppwriteException } from '@appwrite.io/console';
import { browser } from '$app/environment';
+import { getReferrerAndUtmSource } from '$lib/helpers/utm';
function plausible(domain: string): AnalyticsPlugin {
if (!browser) return { name: 'analytics-plugin-plausible' };
@@ -65,6 +66,8 @@ export function trackEvent(name: string, data: object = null): void {
};
}
+ data = { ...data, ...getReferrerAndUtmSource() };
+
if (ENV.DEV || ENV.PREVIEW) {
console.debug(`[Analytics] Event ${name} ${path}`, data);
} else {
diff --git a/src/lib/charts/bar.svelte b/src/lib/charts/bar.svelte
index a447492d27..9d19e36053 100644
--- a/src/lib/charts/bar.svelte
+++ b/src/lib/charts/bar.svelte
@@ -13,7 +13,6 @@
{formatted}
series={series.map((s) => {
s.type = 'bar';
- s.stack = 'total';
s.barMaxWidth = 6;
s.itemStyle = {
borderRadius: [10, 10, 0, 0]
diff --git a/src/lib/charts/index.ts b/src/lib/charts/index.ts
index c73108a134..488756ecb5 100644
--- a/src/lib/charts/index.ts
+++ b/src/lib/charts/index.ts
@@ -1,2 +1,3 @@
export { default as BarChart } from './bar.svelte';
export { default as LineChart } from './line.svelte';
+export { default as Legend, type LegendData } from './legend.svelte';
diff --git a/src/lib/charts/legend.svelte b/src/lib/charts/legend.svelte
new file mode 100644
index 0000000000..2e72c14106
--- /dev/null
+++ b/src/lib/charts/legend.svelte
@@ -0,0 +1,25 @@
+
+
+
+
+
+ {#each legendData as { name, value }, index}
+ {@const formattedValue = typeof value === 'number' ? formatNumberWithCommas(value) : value}
+
+ {name} ({formattedValue})
+
+ {/each}
+
diff --git a/src/lib/commandCenter/panels/ai.svelte b/src/lib/commandCenter/panels/ai.svelte
index d958588071..13326ac8eb 100644
--- a/src/lib/commandCenter/panels/ai.svelte
+++ b/src/lib/commandCenter/panels/ai.svelte
@@ -180,7 +180,7 @@
{#if $isLoading || answer}
-
{getInitials($user.name)}
+
{getInitials($user.name || $user.email)}
{previousQuestion}
@@ -230,7 +230,7 @@
{runtimeDetail.name}
- {#if runtimeDetail.name.toLowerCase() === 'deno'}
- New
- {/if}
+
+
+
diff --git a/src/routes/(console)/account/deleteAccount.svelte b/src/routes/(console)/account/deleteAccount.svelte
index 168d289e4d..863839c663 100644
--- a/src/routes/(console)/account/deleteAccount.svelte
+++ b/src/routes/(console)/account/deleteAccount.svelte
@@ -18,10 +18,10 @@
-
+
- {$user.name}
+ {$user.name || 'User'}
diff --git a/src/routes/(console)/account/organizations/+page.ts b/src/routes/(console)/account/organizations/+page.ts
index cafa665eb8..159e2a6e2e 100644
--- a/src/routes/(console)/account/organizations/+page.ts
+++ b/src/routes/(console)/account/organizations/+page.ts
@@ -3,19 +3,22 @@ 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('console', 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.teams.list(queries)
+ : await sdk.forConsole.billing.listOrganization(queries);
+
return {
offset,
limit,
- organizations: await sdk.forConsole.teams.list([
- Query.offset(offset),
- Query.limit(limit),
- Query.orderDesc('')
- ])
+ organizations
};
};
diff --git a/src/routes/(console)/account/updateName.svelte b/src/routes/(console)/account/updateName.svelte
index 72c521e6c7..06b42fcf9d 100644
--- a/src/routes/(console)/account/updateName.svelte
+++ b/src/routes/(console)/account/updateName.svelte
@@ -12,7 +12,7 @@
let name: string = null;
onMount(async () => {
- name ??= $user.name;
+ name ??= $user.name ?? '';
});
async function updateName() {
diff --git a/src/routes/(console)/apply-credit/+page.svelte b/src/routes/(console)/apply-credit/+page.svelte
index 95af6175f5..55c0045dc7 100644
--- a/src/routes/(console)/apply-credit/+page.svelte
+++ b/src/routes/(console)/apply-credit/+page.svelte
@@ -3,11 +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 { 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';
@@ -16,14 +12,21 @@
WizardSecondaryContent,
WizardSecondaryFooter
} from '$lib/layout';
- import { type PaymentList } from '$lib/sdk/billing';
+ import { type PaymentList, type Plan } from '$lib/sdk/billing';
import { app } from '$lib/stores/app';
+ import { isOrganization } from '$lib/stores/billing.js';
import { addNotification } from '$lib/stores/notifications';
- import { organizationList, type Organization } from '$lib/stores/organization';
+ import {
+ organizationList,
+ type Organization,
+ type OrganizationError
+ } from '$lib/stores/organization';
import { sdk } from '$lib/stores/sdk';
+ import { confirmPayment } from '$lib/stores/stripe.js';
import { ID } from '@appwrite.io/console';
import { onMount } from 'svelte';
import { writable } from 'svelte/store';
+ import { getCampaignImageUrl } from '$routes/(public)/card/helpers';
export let data;
@@ -69,6 +72,12 @@
let couponData = data?.couponData;
let campaign = data?.campaign;
let billingPlan = BillingPlan.PRO;
+ let currentPlan: Plan;
+
+ function isUpgrade() {
+ const newPlan = $page.data.plansInfo.get(billingPlan);
+ return currentPlan && newPlan && currentPlan.order < newPlan.order;
+ }
onMount(async () => {
await loadPaymentMethods();
@@ -82,6 +91,20 @@
if (campaign?.plan) {
billingPlan = campaign.plan;
}
+
+ if ($page.url.searchParams.has('type')) {
+ const type = $page.url.searchParams.get('type');
+ if (type === 'payment_confirmed') {
+ const organizationId = $page.url.searchParams.get('id');
+ collaborators = $page.url.searchParams.get('invites').split(',');
+
+ await sdk.forConsole.billing.validateOrganization(organizationId, collaborators);
+ }
+ }
+
+ if (!selectedOrgId && $organizationList?.total) {
+ selectedOrgId = $organizationList.teams[0].$id;
+ }
});
async function loadPaymentMethods() {
@@ -96,24 +119,33 @@
async function handleSubmit() {
if (!couponForm.checkValidity()) return;
+ isSubmitting.set(true);
try {
- let org: Organization;
+ let org: Organization | OrganizationError;
// Create new org
if (selectedOrgId === newOrgId) {
org = await sdk.forConsole.billing.createOrganization(
newOrgId,
name,
billingPlan,
- paymentMethodId
+ paymentMethodId,
+ null,
+ couponData.code ? couponData.code : null,
+ collaborators,
+ billingBudget,
+ taxId
);
}
+
// Upgrade existing org
- else if (selectedOrg?.billingPlan !== billingPlan) {
+ else if (selectedOrg?.billingPlan !== billingPlan && isUpgrade()) {
org = await sdk.forConsole.billing.updatePlan(
selectedOrg.$id,
billingPlan,
paymentMethodId,
- null
+ null,
+ couponData.code ? couponData.code : null,
+ collaborators
);
}
// Existing pro org
@@ -121,51 +153,47 @@
org = selectedOrg;
}
- // Add coupon
- if (couponData?.code) {
- await sdk.forConsole.billing.addCredit(org.$id, couponData.code);
- }
-
- // Add budget
- if (billingBudget) {
- await sdk.forConsole.billing.updateBudget(org.$id, billingBudget, [75]);
+ if (!isOrganization(org) && org.status === 402) {
+ let clientSecret = org.clientSecret;
+ let params = new URLSearchParams();
+ params.append('type', 'payment_confirmed');
+ params.append('org', org.teamId);
+ for (const [key, value] of $page.url.searchParams.entries()) {
+ if (key !== 'type' && key !== 'id') {
+ params.append(key, value);
+ }
+ }
+ params.append('invites', collaborators.join(','));
+ await confirmPayment(
+ '',
+ clientSecret,
+ paymentMethodId,
+ '/console/apply-credit?' + params.toString()
+ );
+ org = await sdk.forConsole.billing.validateOrganization(org.teamId, collaborators);
}
- // Add collaborators
- if (collaborators?.length) {
- collaborators.forEach(async (collaborator) => {
- await sdk.forConsole.teams.createMembership(
- org.$id,
- ['owner'],
- collaborator,
- undefined,
- undefined,
- `${$page.url.origin}${base}/invite`
- );
+ if (isOrganization(org)) {
+ trackEvent(Submit.CreditRedeem, {
+ coupon: couponData.code,
+ campaign: couponData?.campaign
});
+ await invalidate(Dependencies.ORGANIZATION);
+ await goto(`${base}/organization-${org.$id}`);
+ addNotification({
+ type: 'success',
+ message: 'Credits applied successfully'
+ });
+ await invalidate(Dependencies.ACCOUNT);
}
-
- // Add tax ID
- if (taxId) {
- await sdk.forConsole.billing.updateTaxId(org.$id, taxId);
- }
- trackEvent(Submit.CreditRedeem, {
- coupon: couponData.code,
- campaign: couponData?.campaign
- });
- await invalidate(Dependencies.ORGANIZATION);
- await goto(`${base}/organization-${org.$id}`);
- addNotification({
- type: 'success',
- message: 'Credits applied successfully'
- });
- await invalidate(Dependencies.ACCOUNT);
} catch (e) {
trackError(e, Submit.CreditRedeem);
addNotification({
type: 'error',
message: e.message
});
+ } finally {
+ isSubmitting.set(false);
}
}
@@ -194,6 +222,14 @@
selectedOrg?.billingPlan === BillingPlan.SCALE
? BillingPlan.SCALE
: (campaign?.plan ?? BillingPlan.PRO);
+
+ $: {
+ if (selectedOrgId) {
+ (async () => {
+ currentPlan = await sdk.forConsole.billing.getOrganizationPlan(selectedOrgId);
+ })();
+ }
+ }
@@ -214,7 +250,7 @@
placeholder="Select organization"
id="organization" />
{/if}
- {#if selectedOrgId && (selectedOrg?.billingPlan !== BillingPlan.PRO || !selectedOrg?.paymentMethodId)}
+ {#if selectedOrgId && (selectedOrg?.billingPlan === BillingPlan.FREE || !selectedOrg?.paymentMethodId)}
{#if selectedOrgId === newOrgId}
@@ -290,12 +326,12 @@
{:else if selectedOrgId}
-
+ {couponData}>
{#if campaign?.template === 'review' && (campaign?.cta || campaign?.claimed || campaign?.unclaimed)}
{campaign?.cta}
@@ -308,7 +344,7 @@
{/if}
-
+
{/if}
@@ -330,7 +366,7 @@
{#if selectedOrgId === newOrgId}
Create Organization
{:else}
- Apply Credits
+ Apply credits
{/if}
diff --git a/src/routes/(console)/apply-credit/+page.ts b/src/routes/(console)/apply-credit/+page.ts
index 138e59ee3a..362a3adff7 100644
--- a/src/routes/(console)/apply-credit/+page.ts
+++ b/src/routes/(console)/apply-credit/+page.ts
@@ -3,16 +3,15 @@ import type { Coupon } from '$lib/sdk/billing.js';
import type { Campaign } from '$lib/stores/campaigns.js';
import { sdk } from '$lib/stores/sdk.js';
import { redirect } from '@sveltejs/kit';
-import type { PageLoad } from './$types';
-export const load: PageLoad = async ({ url }) => {
+export const load = async ({ url }) => {
// Has promo code
if (url.searchParams.has('code')) {
let couponData: Coupon;
let campaign: Campaign;
const code = url.searchParams.get('code');
try {
- couponData = await sdk.forConsole.billing.getCoupon(code);
+ couponData = await sdk.forConsole.billing.getCouponAccount(code);
if (couponData.campaign) {
campaign = await sdk.forConsole.billing.getCampaign(couponData.campaign);
}
diff --git a/src/routes/(console)/create-organization/+page.svelte b/src/routes/(console)/create-organization/+page.svelte
index 63cc1f2100..a9a92035ee 100644
--- a/src/routes/(console)/create-organization/+page.svelte
+++ b/src/routes/(console)/create-organization/+page.svelte
@@ -3,12 +3,8 @@
import { base } from '$app/paths';
import { page } from '$app/stores';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
- import {
- EstimatedTotalBox,
- PlanComparisonBox,
- PlanSelection,
- SelectPaymentMethod
- } from '$lib/components/billing';
+ import { PlanComparisonBox, SelectPaymentMethod, SelectPlan } 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';
@@ -19,10 +15,15 @@
WizardSecondaryFooter
} from '$lib/layout';
import type { Coupon, PaymentList } from '$lib/sdk/billing';
- import { tierToPlan } from '$lib/stores/billing';
+ import { isOrganization, tierToPlan } from '$lib/stores/billing';
import { addNotification } from '$lib/stores/notifications';
- import { organizationList, type Organization } from '$lib/stores/organization';
+ import {
+ organizationList,
+ type OrganizationError,
+ 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';
@@ -60,7 +61,7 @@
if ($page.url.searchParams.has('coupon')) {
const coupon = $page.url.searchParams.get('coupon');
try {
- const response = await sdk.forConsole.billing.getCoupon(coupon);
+ const response = await sdk.forConsole.billing.getCouponAccount(coupon);
couponData = response;
} catch (e) {
couponData = {
@@ -86,6 +87,14 @@
) {
billingPlan = BillingPlan.PRO;
}
+ if ($page.url.searchParams.has('type')) {
+ const type = $page.url.searchParams.get('type');
+ if (type === 'payment_confirmed') {
+ const organizationId = $page.url.searchParams.get('id');
+ const invites = $page.url.searchParams.get('invites').split(',');
+ await validate(organizationId, invites);
+ }
+ }
});
async function loadPaymentMethods() {
@@ -93,9 +102,29 @@
paymentMethodId = methods.paymentMethods.find((method) => !!method?.last4)?.$id ?? null;
}
+ async function validate(organizationId: string, invites: string[]) {
+ try {
+ const 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;
+ let org: Organization | OrganizationError;
if (billingPlan === BillingPlan.FREE) {
org = await sdk.forConsole.billing.createOrganization(
@@ -111,53 +140,49 @@
name,
billingPlan,
paymentMethodId,
- null
+ null,
+ couponData.code ? couponData.code : null,
+ collaborators,
+ billingBudget,
+ taxId
);
- //Add budget
- if (billingBudget) {
- 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,
- ['developer'],
- collaborator,
- undefined,
- undefined,
- `${$page.url.origin}${base}/invite`
- );
- });
- }
-
- // Add tax ID
- if (taxId) {
- await sdk.forConsole.billing.updateTaxId(org.$id, taxId);
+ if (!isOrganization(org) && org.status === 402) {
+ let clientSecret = org.clientSecret;
+ let params = new URLSearchParams();
+ params.append('type', 'payment_confirmed');
+ params.append('id', org.teamId);
+ for (const [key, value] of $page.url.searchParams.entries()) {
+ if (key !== 'type' && key !== 'id') {
+ params.append(key, value);
+ }
+ }
+ params.append('invites', collaborators.join(','));
+ await confirmPayment(
+ '',
+ clientSecret,
+ paymentMethodId,
+ '/console/create-organization?' + params.toString()
+ );
+ await validate(org.teamId, collaborators);
}
}
trackEvent(Submit.OrganizationCreate, {
plan: tierToPlan(billingPlan)?.name,
- budget_cap_enabled: !!billingBudget,
+ budget_cap_enabled: billingBudget !== null,
members_invited: collaborators?.length
});
- await invalidate(Dependencies.ACCOUNT);
- await preloadData(`${base}/organization-${org.$id}`);
- await goto(`${base}/organization-${org.$id}`);
- addNotification({
- type: 'success',
- message: `${name ?? 'Organization'} has been created`
- });
+ if (isOrganization(org)) {
+ await invalidate(Dependencies.ACCOUNT);
+ await preloadData(`${base}/organization-${org.$id}`);
+ await goto(`${base}/organization-${org.$id}`);
+ addNotification({
+ type: 'success',
+ message: `${org.name ?? 'Organization'} has been created`
+ });
+ }
} catch (e) {
addNotification({
type: 'error',
@@ -193,7 +218,7 @@
For more details on our plans, visit our
.
-
+
{#if billingPlan !== BillingPlan.FREE}
{#if billingPlan !== BillingPlan.FREE}
-
+
{:else}
{/if}
diff --git a/src/routes/(console)/createOrganization.svelte b/src/routes/(console)/createOrganization.svelte
index 3c4d7b2194..6547a5c518 100644
--- a/src/routes/(console)/createOrganization.svelte
+++ b/src/routes/(console)/createOrganization.svelte
@@ -1,6 +1,5 @@
@@ -194,7 +197,7 @@
{/if}
- {#if isCloud && regions}
+ {#if isCloud && $regionsStore?.regions}
{@const region = findRegion(project)}
{region?.name}
diff --git a/src/routes/(console)/organization-[organization]/billing/+page.svelte b/src/routes/(console)/organization-[organization]/billing/+page.svelte
index 017a1dec08..2d3772358b 100644
--- a/src/routes/(console)/organization-[organization]/billing/+page.svelte
+++ b/src/routes/(console)/organization-[organization]/billing/+page.svelte
@@ -1,7 +1,6 @@