mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
fix: delete flow, upgrade flow, QA
This commit is contained in:
@@ -115,7 +115,7 @@
|
||||
|
||||
<div class="main-header-end">
|
||||
<nav class="u-flex is-only-desktop u-cross-center">
|
||||
{#if isCloud && $organization?.billingPlan !== 'tier-2' && !$page.url.pathname.startsWith('/console/account')}
|
||||
{#if isCloud && $organization?.billingPlan === 'tier-0' && !$page.url.pathname.startsWith('/console/account')}
|
||||
<Button secondary on:click={() => wizard.start(ChangeOrganizationTierCloud)}>
|
||||
Upgrade
|
||||
</Button>
|
||||
|
||||
+16
-1
@@ -277,6 +277,22 @@ export class Billing {
|
||||
);
|
||||
}
|
||||
|
||||
async deleteOrganization(organizationId: string): Promise<Organization> {
|
||||
const path = `/organizations/${organizationId}`;
|
||||
const params = {
|
||||
organizationId
|
||||
};
|
||||
const uri = new URL(this.client.config.endpoint + path);
|
||||
return await this.client.call(
|
||||
'DELETE',
|
||||
uri,
|
||||
{
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
async getPlan(organizationId: string): Promise<Plan> {
|
||||
const path = `/organizations/${organizationId}/plan`;
|
||||
const params = {
|
||||
@@ -482,7 +498,6 @@ export class Billing {
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: add date range
|
||||
async listUsage(
|
||||
organizationId: string,
|
||||
startDate: string = null,
|
||||
|
||||
@@ -87,25 +87,20 @@ export const failedInvoice = cachedStore<
|
||||
});
|
||||
|
||||
export type TierData = {
|
||||
price: number;
|
||||
name: string;
|
||||
description: string;
|
||||
collaboratorPrice?: number;
|
||||
};
|
||||
|
||||
export const tierFree: TierData = {
|
||||
price: 0,
|
||||
name: 'Starter',
|
||||
description: 'For personal, passion projects.'
|
||||
};
|
||||
|
||||
export const tierPro: TierData = {
|
||||
price: 15,
|
||||
name: 'Pro',
|
||||
description: 'For pro developers and production projects that need the ability to scale.'
|
||||
};
|
||||
export const tierScale: TierData = {
|
||||
price: 685,
|
||||
name: 'Scale',
|
||||
description: 'For scaling teams that need dedicated support.'
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@ export type Organization = Models.Team<Record<string, unknown>> & {
|
||||
budgetAlerts: number[];
|
||||
paymentMethodId: string;
|
||||
backupPaymentMethodId: string;
|
||||
markedForDeletion: boolean;
|
||||
billingLimits: BillingLimits;
|
||||
billingCurrentInvoiceDate: string;
|
||||
billingNextInvoiceDate: string;
|
||||
|
||||
@@ -416,8 +416,7 @@
|
||||
</svelte:fragment>
|
||||
</HeaderAlert>
|
||||
{/if}
|
||||
<!-- TODO: add variable from backend that indicates org is due to be deleted -->
|
||||
{#if false}
|
||||
{#if $organization?.markedForDeletion && !$page.url.pathname.includes('/console/account')}
|
||||
<HeaderAlert title="Organization flagged for deletion">
|
||||
<svelte:fragment>
|
||||
All existing projects in the {$organization.name} organization have been paused.
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
import Delete from './deleteOrganization.svelte';
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
|
||||
export let data;
|
||||
let name: string;
|
||||
let showDelete = false;
|
||||
|
||||
@@ -89,4 +90,4 @@
|
||||
{/if}
|
||||
</Container>
|
||||
|
||||
<Delete bind:showDelete />
|
||||
<Delete bind:showDelete invoices={data.invoices} />
|
||||
|
||||
@@ -2,9 +2,11 @@ import type { PageLoad } from './$types';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
|
||||
export const load: PageLoad = async ({ depends }) => {
|
||||
export const load: PageLoad = async ({ depends, parent }) => {
|
||||
const { organization } = await parent();
|
||||
depends(Dependencies.ORGANIZATION);
|
||||
return {
|
||||
projects: await sdk.forConsole.projects.list()
|
||||
projects: await sdk.forConsole.projects.list(),
|
||||
invoices: await sdk.forConsole.billing.listInvoices(organization.$id)
|
||||
};
|
||||
};
|
||||
|
||||
@@ -10,21 +10,26 @@
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { projects } from '../store';
|
||||
import { toLocaleDate } from '$lib/helpers/date';
|
||||
import { isCloud } from '$lib/system';
|
||||
import type { InvoiceList } from '$lib/sdk/billing';
|
||||
|
||||
export let showDelete = false;
|
||||
export let invoices: InvoiceList;
|
||||
let error: string = null;
|
||||
|
||||
//TODO: replace variable with value from method
|
||||
const TEMPORARY_SET_FOR_DELETION = true;
|
||||
|
||||
async function deleteOrg() {
|
||||
try {
|
||||
await sdk.forConsole.teams.delete($organization.$id);
|
||||
await invalidate(Dependencies.ACCOUNT);
|
||||
if (isCloud) {
|
||||
await sdk.forConsole.billing.deleteOrganization($organization.$id);
|
||||
} else {
|
||||
await sdk.forConsole.teams.delete($organization.$id);
|
||||
}
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `${$organization.name} has been deleted`
|
||||
});
|
||||
await invalidate(Dependencies.ACCOUNT);
|
||||
await invalidate(Dependencies.ORGANIZATION);
|
||||
trackEvent(Submit.OrganizationDelete);
|
||||
if ($organizationList?.total > 1) {
|
||||
goto(`${base}/console/`);
|
||||
@@ -37,6 +42,8 @@
|
||||
trackError(e, Submit.OrganizationDelete);
|
||||
}
|
||||
}
|
||||
|
||||
$: upcomingInvoice = invoices.invoices.find((i) => i.status === 'upcoming' && i.amount > 0);
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
@@ -47,7 +54,7 @@
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
headerDivider={false}>
|
||||
{#if TEMPORARY_SET_FOR_DELETION}
|
||||
{#if upcomingInvoice}
|
||||
<p data-private>
|
||||
The organization <b>{$organization.name}</b> will be flagged for deletion.
|
||||
</p>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import { VARS } from '$lib/system';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
|
||||
onDestroy(() => {
|
||||
$supportData = {
|
||||
@@ -37,7 +38,13 @@
|
||||
firstName: $user?.name ?? '',
|
||||
message: $supportData.message,
|
||||
tags: ['cloud'],
|
||||
customFields: [{ id: '41612', value: $supportData.category }]
|
||||
customFields: [
|
||||
{ id: '41612', value: $supportData.category },
|
||||
{ id: '48493', value: $user?.name ?? '' },
|
||||
{ id: '48492', value: $organization.$id ?? '' },
|
||||
// { id: '48491', value: $user?.projectId ?? '' },
|
||||
{ id: '48490', value: $user?.$id ?? '' }
|
||||
]
|
||||
})
|
||||
});
|
||||
trackEvent(Submit.SupportTicket);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { Pill } from '$lib/elements';
|
||||
import { InputText, FormList } from '$lib/elements/forms';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { tierFree, tierPro, tierScale } from '$lib/stores/billing';
|
||||
import { plansInfo, tierFree, tierPro, tierScale } from '$lib/stores/billing';
|
||||
import { organizationList, type Organization } from '$lib/stores/organization';
|
||||
import { updateStepStatus } from '$lib/stores/wizard';
|
||||
import { createOrganization, createOrgSteps } from './store';
|
||||
@@ -28,6 +28,10 @@
|
||||
$createOrgSteps = updateStepStatus($createOrgSteps, 3, false);
|
||||
$createOrgSteps = updateStepStatus($createOrgSteps, 4, false);
|
||||
}
|
||||
|
||||
$: freePlan = $plansInfo.plans.find((p) => p.$id === 'tier-0');
|
||||
$: proPlan = $plansInfo.plans.find((p) => p.$id === 'tier-1');
|
||||
$: scalePlan = $plansInfo.plans.find((p) => p.$id === 'tier-2');
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
@@ -81,7 +85,7 @@
|
||||
class="u-flex u-flex-vertical u-gap-4 u-width-full-line"
|
||||
class:u-opacity-50={disabled}>
|
||||
<h4 class="body-text-2 u-bold">
|
||||
{tierFree.name} - ${tierFree.price}/month
|
||||
{tierFree.name} - ${freePlan?.price}/month
|
||||
</h4>
|
||||
<p class="u-color-text-gray u-small">{tierFree.description}</p>
|
||||
</div>
|
||||
@@ -94,7 +98,7 @@
|
||||
<svelte:fragment slot="custom">
|
||||
<div class="u-flex u-flex-vertical u-gap-4 u-width-full-line">
|
||||
<h4 class="body-text-2 u-bold">
|
||||
{tierPro.name} - ${tierPro.price}/month per organization member + exta
|
||||
{tierPro.name} - ${proPlan.price}/month per organization member + exta
|
||||
usage
|
||||
</h4>
|
||||
<p class="u-color-text-gray u-small">
|
||||
@@ -116,7 +120,7 @@
|
||||
class="u-flex u-flex-vertical u-gap-4 u-width-full-line"
|
||||
class:u-opacity-50={disabled}>
|
||||
<h4 class="body-text-2 u-bold">
|
||||
{tierScale.name} - ${tierScale.price}/month + extra usage
|
||||
{tierScale.name} - ${scalePlan.price}/month + extra usage
|
||||
</h4>
|
||||
<p class="u-color-text-gray u-small">
|
||||
{tierScale.description}
|
||||
|
||||
@@ -86,6 +86,10 @@
|
||||
$changeOrganizationTier.billingPlan = 'tier-1';
|
||||
}
|
||||
});
|
||||
|
||||
$: freePlan = $plansInfo.plans.find((p) => p.$id === 'tier-0');
|
||||
$: proPlan = $plansInfo.plans.find((p) => p.$id === 'tier-1');
|
||||
$: scalePlan = $plansInfo.plans.find((p) => p.$id === 'tier-2');
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
@@ -114,7 +118,7 @@
|
||||
class="u-flex u-flex-vertical u-gap-4 u-width-full-line"
|
||||
class:u-opacity-50={disabled}>
|
||||
<h4 class="body-text-2 u-bold">
|
||||
{tierFree.name} - ${tierFree.price}/month
|
||||
{tierFree.name} - ${freePlan.price}/month
|
||||
</h4>
|
||||
<p class="u-color-text-gray u-small">{tierFree.description}</p>
|
||||
</div>
|
||||
@@ -139,7 +143,7 @@
|
||||
class="u-flex u-flex-vertical u-gap-4 u-width-full-line"
|
||||
class:u-opacity-50={disabled}>
|
||||
<h4 class="body-text-2 u-bold">
|
||||
{tierPro.name} - ${tierPro.price}/month per organization member + extra
|
||||
{tierPro.name} - ${proPlan.price}/month per organization member + extra
|
||||
usage
|
||||
</h4>
|
||||
<p class="u-color-text-gray u-small">
|
||||
@@ -167,7 +171,7 @@
|
||||
class="u-flex u-flex-vertical u-gap-4 u-width-full-line"
|
||||
class:u-opacity-50={disabled}>
|
||||
<h4 class="body-text-2 u-bold">
|
||||
{tierScale.name} - ${tierScale.price}/month + extra usage
|
||||
{tierScale.name} - ${scalePlan.price}/month + extra usage
|
||||
</h4>
|
||||
<p class="u-color-text-gray u-small">
|
||||
{tierScale.description}
|
||||
|
||||
@@ -167,11 +167,21 @@
|
||||
<p class="text">Additional members ({collaboratorsNumber})</p>
|
||||
<p class="text">${collaboratorPrice * collaboratorsNumber}</p>
|
||||
</span>
|
||||
{#if showCoupon && couponData?.status !== 'error'}
|
||||
<span class="u-flex u-main-space-between">
|
||||
<p class="text">Credits applied ({couponData.credits})</p>
|
||||
<p class="text">-${couponData.credits}</p>
|
||||
</span>
|
||||
{/if}
|
||||
{/if}
|
||||
<div class="u-sep-block-start" />
|
||||
<span class="u-flex u-main-space-between">
|
||||
<p class="text">Estimated total</p>
|
||||
<p class="text">${totalExpences}</p>
|
||||
<p class="text">Estimated total (in USD)</p>
|
||||
<p class="text">
|
||||
${showCoupon && couponData?.status !== 'error'
|
||||
? totalExpences - couponData.credits || 0
|
||||
: totalExpences}
|
||||
</p>
|
||||
</span>
|
||||
|
||||
<p class="text u-margin-block-start-16">
|
||||
|
||||
Reference in New Issue
Block a user