diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 890c78c8e..a0490076e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -20,7 +20,7 @@ jobs: with: node-version: 20 - name: Audit dependencies - run: npm audit --audit-level low + run: npm audit --audit-level critical - name: Install dependencies run: npm ci - name: Svelte Diagnostics diff --git a/src/lib/actions/analytics.ts b/src/lib/actions/analytics.ts index 7c957adf8..4c9cb3be8 100644 --- a/src/lib/actions/analytics.ts +++ b/src/lib/actions/analytics.ts @@ -269,6 +269,7 @@ export enum Submit { PaymentMethodUpdate = 'submit_payment_method_update', PaymentMethodDelete = 'submit_payment_method_delete', RetryPayment = 'submit_retry_payment', + VerifyPayment = 'submit_verify_payment', BillingAddressCreate = 'submit_billing_address_create', BillingAddressUpdate = 'submit_billing_address_update', BillingAddressDelete = 'submit_billing_address_delete', diff --git a/src/lib/components/billing/alerts/paymentMandate.svelte b/src/lib/components/billing/alerts/paymentMandate.svelte new file mode 100644 index 000000000..9799ddcc3 --- /dev/null +++ b/src/lib/components/billing/alerts/paymentMandate.svelte @@ -0,0 +1,26 @@ + + +{#if $paymentMissingMandate && $paymentMissingMandate.country === 'in' && $paymentMissingMandate.mandateId === null && !$page.url.pathname.includes('/console/account')} + + The payment method for {$organization.name} needs to be verified. + + + + +{/if} diff --git a/src/lib/components/permissions/team.svelte b/src/lib/components/permissions/team.svelte index 5056e1186..e8d900df2 100644 --- a/src/lib/components/permissions/team.svelte +++ b/src/lib/components/permissions/team.svelte @@ -6,6 +6,8 @@ import { AvatarInitials, EmptySearch, Modal, PaginationInline } from '..'; import type { Writable } from 'svelte/store'; import type { Permission } from './permissions.svelte'; + import Table from '$lib/elements/table/table.svelte'; + import { TableBody, TableCell, TableRow } from '$lib/elements/table'; export let show: boolean; export let groups: Writable>; @@ -77,13 +79,13 @@ bind:value={search} /> {#if results?.teams?.length}
- - +
+ {#each results.teams as team (team.$id)} {@const role = `team:${team.$id}`} {@const exists = $groups.has(role)} - - - - + + {/each} - -
+ + onSelection(event, role)} /> - + + -
+ +

Total results: {results?.total}

diff --git a/src/lib/elements/table/table.svelte b/src/lib/elements/table/table.svelte index 503235d98..c758b7c82 100644 --- a/src/lib/elements/table/table.svelte +++ b/src/lib/elements/table/table.svelte @@ -3,11 +3,15 @@ export let noStyles = false; export let style = ''; export let transparent = false; + export let isAutoLayout = false; + export let tag: 'div' | 'table' = 'div'; export let dense = false; -
-
+ diff --git a/src/lib/elements/table/tableScroll.svelte b/src/lib/elements/table/tableScroll.svelte index 5a773cd85..8c2f43b3a 100644 --- a/src/lib/elements/table/tableScroll.svelte +++ b/src/lib/elements/table/tableScroll.svelte @@ -5,7 +5,9 @@ export let noMargin = false; export let style = ''; export let transparent = false; + export let noStyles = false; export let dense = false; + let isOverflowing = false; const hasOverflow: Action = (node) => { @@ -45,6 +47,7 @@
(isOverflowing = v)}> {#if hasProjectLimitation} (showDropdown = !showDropdown)}> - {total}/{limit} - {title} used + {total}/{limit} created {:else} (showDropdown = !showDropdown)}> - {title} limited + Limits applied {/if} {#if hasProjectLimitation}

- Your are limited to {limit} + You are limited to {limit} {title.toLocaleLowerCase()} per project on the {tier} plan. {#if $organization?.billingPlan === BillingPlan.STARTER} - for addtional {title.toLocaleLowerCase()}. + for additional {title.toLocaleLowerCase()}. {/if}

{:else if hasUsageFees} diff --git a/src/lib/sdk/billing.ts b/src/lib/sdk/billing.ts index f21ea5ec6..9bfb2ef45 100644 --- a/src/lib/sdk/billing.ts +++ b/src/lib/sdk/billing.ts @@ -18,6 +18,7 @@ export type PaymentMethodData = { clientSecret: string; failed: boolean; name: string; + mandateId?: string; }; export type PaymentList = { @@ -865,6 +866,26 @@ export class Billing { ); } + async setupPaymentMandate( + organizationId: string, + paymentMethodId: string + ): Promise { + const path = `/account/payment-methods/${paymentMethodId}/setup`; + const params = { + organizationId, + paymentMethodId + }; + const uri = new URL(this.client.config.endpoint + path); + return await this.client.call( + 'post', + uri, + { + 'content-type': 'application/json' + }, + params + ); + } + async listAddresses(queries: string[] = []): Promise { const path = `/account/billing-addresses`; const params = { diff --git a/src/lib/stores/billing.ts b/src/lib/stores/billing.ts index 141b31f93..b2f00db39 100644 --- a/src/lib/stores/billing.ts +++ b/src/lib/stores/billing.ts @@ -2,7 +2,14 @@ import { page } from '$app/stores'; import { derived, get, writable } from 'svelte/store'; import { sdk } from './sdk'; import { organization, type Organization } from './organization'; -import type { InvoiceList, AddressesList, Invoice, PaymentList, PlansMap } from '$lib/sdk/billing'; +import type { + InvoiceList, + AddressesList, + Invoice, + PaymentList, + PlansMap, + PaymentMethodData +} from '$lib/sdk/billing'; import { isCloud } from '$lib/system'; import { cachedStore } from '$lib/helpers/cache'; import { Query } from '@appwrite.io/console'; @@ -14,6 +21,7 @@ import { base } from '$app/paths'; import { activeHeaderAlert, orgMissingPaymentMethod } from '$routes/console/store'; import MarkedForDeletion from '$lib/components/billing/alerts/markedForDeletion.svelte'; import { BillingPlan } from '$lib/constants'; +import PaymentMandate from '$lib/components/billing/alerts/paymentMandate.svelte'; import MissingPaymentMethod from '$lib/components/billing/alerts/missingPaymentMethod.svelte'; export type Tier = 'tier-0' | 'tier-1' | 'tier-2'; @@ -262,11 +270,28 @@ export function checkForMarkedForDeletion(org: Organization) { id: 'markedForDeletion', component: MarkedForDeletion, show: true, - importance: 5 + importance: 10 }); } } +export const paymentMissingMandate = writable(null); + +export async function checkForMandate(org: Organization) { + const paymentId = org.paymentMethodId ?? org.backupPaymentMethodId; + const paymentMethod = await sdk.forConsole.billing.getPaymentMethod(paymentId); + if (paymentMethod.mandateId === null && paymentMethod.country === 'in') { + headerAlert.add({ + id: 'paymentMandate', + component: PaymentMandate, + show: true, + importance: 8 + }); + activeHeaderAlert.set(headerAlert.get()); + paymentMissingMandate.set(paymentMethod); + } +} + export async function checkForMissingPaymentMethod() { const orgs = await sdk.forConsole.billing.listOrganization([ Query.notEqual('billingPlan', BillingPlan.STARTER), diff --git a/src/lib/stores/oauth-providers.ts b/src/lib/stores/oauth-providers.ts index 88fb64aa9..8d08dffdb 100644 --- a/src/lib/stores/oauth-providers.ts +++ b/src/lib/stores/oauth-providers.ts @@ -1,15 +1,15 @@ import type { Models } from '@appwrite.io/console'; import type { SvelteComponent } from 'svelte'; import { writable } from 'svelte/store'; -import Apple from '../../routes/console/project-[project]/auth/appleOAuth.svelte'; -import Auth0 from '../../routes/console/project-[project]/auth/auth0OAuth.svelte'; -import Authentik from '../../routes/console/project-[project]/auth/authentikOAuth.svelte'; -import GitLab from '../../routes/console/project-[project]/auth/gitlabOAuth.svelte'; -import Google from '../../routes/console/project-[project]/auth/googleOAuth.svelte'; -import Main from '../../routes/console/project-[project]/auth/mainOAuth.svelte'; -import Microsoft from '../../routes/console/project-[project]/auth/microsoftOAuth.svelte'; -import Oidc from '../../routes/console/project-[project]/auth/oidcOAuth.svelte'; -import Okta from '../../routes/console/project-[project]/auth/oktaOAuth.svelte'; +import Apple from '../../routes/console/project-[project]/auth/(providers)/appleOAuth.svelte'; +import Auth0 from '../../routes/console/project-[project]/auth/(providers)/auth0OAuth.svelte'; +import Authentik from '../../routes/console/project-[project]/auth/(providers)/authentikOAuth.svelte'; +import GitLab from '../../routes/console/project-[project]/auth/(providers)/gitlabOAuth.svelte'; +import Google from '../../routes/console/project-[project]/auth/(providers)/googleOAuth.svelte'; +import Main from '../../routes/console/project-[project]/auth/(providers)/mainOAuth.svelte'; +import Microsoft from '../../routes/console/project-[project]/auth/(providers)/microsoftOAuth.svelte'; +import Oidc from '../../routes/console/project-[project]/auth/(providers)/oidcOAuth.svelte'; +import Okta from '../../routes/console/project-[project]/auth/(providers)/oktaOAuth.svelte'; export type Provider = Models.AuthProvider & { icon: string; diff --git a/src/lib/stores/stripe.ts b/src/lib/stores/stripe.ts index fd89db004..959b46ffc 100644 --- a/src/lib/stores/stripe.ts +++ b/src/lib/stores/stripe.ts @@ -5,6 +5,7 @@ import { get, writable } from 'svelte/store'; import type { PaymentMethodData } from '$lib/sdk/billing'; import { Submit, trackError, trackEvent } from '$lib/actions/analytics'; import { addNotification } from './notifications'; +import { organization } from './organization'; export const stripe = writable(); let paymentMethod: PaymentMethodData; @@ -68,7 +69,8 @@ export async function submitStripeCard(name: string, urlRoute?: string) { clientSecret, confirmParams: { return_url: `${baseUrl}${ - urlRoute ?? `organization/billing?clientSecret=${clientSecret}` + urlRoute ?? + `organization-${get(organization).$id}/billing?clientSecret=${clientSecret}` }`, payment_method_data: { billing_details: { @@ -138,6 +140,45 @@ export async function confirmPayment( } } +export async function confirmSetup( + clientSecret: string, + paymentMethodId: string, + urlRoute?: string +) { + const baseUrl = 'https://cloud.appwrite.io/console/'; + + const { setupIntent, error } = await get(stripe).confirmCardSetup(clientSecret, { + payment_method: paymentMethodId, + return_url: `${baseUrl}${ + urlRoute ?? `organization-${get(organization).$id}/billing?clientSecret=${clientSecret}` + }` + }); + + if (error) { + const e = new Error(error.message); + trackError(e, Submit.VerifyPayment); + addNotification({ + message: error.message, + type: 'error' + }); + } + + if (setupIntent && setupIntent.status === 'succeeded') { + trackEvent(Submit.VerifyPayment); + addNotification({ + message: 'Payment method verified successfully', + type: 'success' + }); + } else { + const e = new Error('Something went wrong. Please try again later.'); + trackError(e, Submit.VerifyPayment); + addNotification({ + message: e.message, + type: 'error' + }); + } +} + const apperanceLight = { variables: { colorPrimary: '#606a7b', diff --git a/src/routes/console/+layout.svelte b/src/routes/console/+layout.svelte index b6148d7bd..3cb22443e 100644 --- a/src/routes/console/+layout.svelte +++ b/src/routes/console/+layout.svelte @@ -20,6 +20,7 @@ calculateTrialDay, paymentExpired, checkForMarkedForDeletion, + checkForMandate, checkForMissingPaymentMethod } from '$lib/stores/billing'; import { goto } from '$app/navigation'; @@ -262,6 +263,7 @@ await checkForUsageLimit(org); checkForMarkedForDeletion(org); await checkPaymentAuthorizationRequired(org); + await checkForMandate(org); } }); diff --git a/src/routes/console/account/organizations/+page.svelte b/src/routes/console/account/organizations/+page.svelte index a82d76a58..0216b5621 100644 --- a/src/routes/console/account/organizations/+page.svelte +++ b/src/routes/console/account/organizations/+page.svelte @@ -82,7 +82,7 @@ FREE {/if} - {#if organization?.billingTrialStartDate && $daysLeftInTrial > 0} + {#if organization?.billingTrialStartDate && $daysLeftInTrial > 0 && organization.billingPlan !== BillingPlan.STARTER}
+ option.value === $changeOrganizationTier.feedbackDowngradeReason + )?.label, + orgId: $organization.$id, + userId: $user.$id, + message: $changeOrganizationTier?.feedbackMessage ?? '' + }) + }); addNotification({ type: 'success', diff --git a/src/routes/console/organization-[organization]/billing/paymentHistory.svelte b/src/routes/console/organization-[organization]/billing/paymentHistory.svelte index eb3ea4b31..a8caf4348 100644 --- a/src/routes/console/organization-[organization]/billing/paymentHistory.svelte +++ b/src/routes/console/organization-[organization]/billing/paymentHistory.svelte @@ -65,7 +65,7 @@

{#if invoiceList.total - 1 > 0} - + Due Date Status @@ -94,7 +94,7 @@ {formatCurrency(invoice.amount)} - + FREE {/if} - {#if isCloud && $organization?.billingTrialStartDate && $daysLeftInTrial > 0} + {#if isCloud && $organization?.billingTrialStartDate && $daysLeftInTrial > 0 && $organization.billingPlan !== BillingPlan.STARTER}
- + {provider.name} OAuth2 Settings

diff --git a/src/routes/console/project-[project]/auth/auth0OAuth.svelte b/src/routes/console/project-[project]/auth/(providers)/auth0OAuth.svelte similarity index 95% rename from src/routes/console/project-[project]/auth/auth0OAuth.svelte rename to src/routes/console/project-[project]/auth/(providers)/auth0OAuth.svelte index 51a25c834..0607180a5 100644 --- a/src/routes/console/project-[project]/auth/auth0OAuth.svelte +++ b/src/routes/console/project-[project]/auth/(providers)/auth0OAuth.svelte @@ -5,9 +5,10 @@ import type { Provider } from '$lib/stores/oauth-providers'; import { sdk } from '$lib/stores/sdk'; import { onMount } from 'svelte'; - import { updateOAuth } from './updateOAuth'; + import { updateOAuth } from '../updateOAuth'; export let provider: Provider; + export let show = false; let appId: string = null; let enabled: boolean = null; @@ -38,7 +39,7 @@ : provider.secret; - + {provider.name} OAuth2 Settings

diff --git a/src/routes/console/project-[project]/auth/authentikOAuth.svelte b/src/routes/console/project-[project]/auth/(providers)/authentikOAuth.svelte similarity index 95% rename from src/routes/console/project-[project]/auth/authentikOAuth.svelte rename to src/routes/console/project-[project]/auth/(providers)/authentikOAuth.svelte index 7df8877a5..f7d6dcd37 100644 --- a/src/routes/console/project-[project]/auth/authentikOAuth.svelte +++ b/src/routes/console/project-[project]/auth/(providers)/authentikOAuth.svelte @@ -5,9 +5,10 @@ import type { Provider } from '$lib/stores/oauth-providers'; import { sdk } from '$lib/stores/sdk'; import { onMount } from 'svelte'; - import { updateOAuth } from './updateOAuth'; + import { updateOAuth } from '../updateOAuth'; export let provider: Provider; + export let show = false; let appId: string = null; let enabled: boolean = null; @@ -38,7 +39,7 @@ : provider.secret; - + {provider.name} OAuth2 Settings

diff --git a/src/routes/console/project-[project]/auth/gitlabOAuth.svelte b/src/routes/console/project-[project]/auth/(providers)/gitlabOAuth.svelte similarity index 95% rename from src/routes/console/project-[project]/auth/gitlabOAuth.svelte rename to src/routes/console/project-[project]/auth/(providers)/gitlabOAuth.svelte index e93d4ae8f..95f80675e 100644 --- a/src/routes/console/project-[project]/auth/gitlabOAuth.svelte +++ b/src/routes/console/project-[project]/auth/(providers)/gitlabOAuth.svelte @@ -5,9 +5,10 @@ import type { Provider } from '$lib/stores/oauth-providers'; import { sdk } from '$lib/stores/sdk'; import { onMount } from 'svelte'; - import { updateOAuth } from './updateOAuth'; + import { updateOAuth } from '../updateOAuth'; export let provider: Provider; + export let show = false; let appId: string = null; let enabled: boolean = null; @@ -36,7 +37,7 @@ clientSecret && endpoint ? JSON.stringify({ clientSecret, endpoint }) : provider.secret; - + {provider.name} OAuth2 Settings

diff --git a/src/routes/console/project-[project]/auth/googleOAuth.svelte b/src/routes/console/project-[project]/auth/(providers)/googleOAuth.svelte similarity index 95% rename from src/routes/console/project-[project]/auth/googleOAuth.svelte rename to src/routes/console/project-[project]/auth/(providers)/googleOAuth.svelte index c06c99b6f..defd59038 100644 --- a/src/routes/console/project-[project]/auth/googleOAuth.svelte +++ b/src/routes/console/project-[project]/auth/(providers)/googleOAuth.svelte @@ -5,9 +5,10 @@ import type { Provider } from '$lib/stores/oauth-providers'; import { sdk } from '$lib/stores/sdk'; import { onMount } from 'svelte'; - import { updateOAuth } from './updateOAuth'; + import { updateOAuth } from '../updateOAuth'; export let provider: Provider; + export let show = false; const projectId = $page.params.project; @@ -34,7 +35,7 @@ }; - + {provider.name} OAuth2 Settings

diff --git a/src/routes/console/project-[project]/auth/mainOAuth.svelte b/src/routes/console/project-[project]/auth/(providers)/mainOAuth.svelte similarity index 94% rename from src/routes/console/project-[project]/auth/mainOAuth.svelte rename to src/routes/console/project-[project]/auth/(providers)/mainOAuth.svelte index f8c11649e..184fcd00c 100644 --- a/src/routes/console/project-[project]/auth/mainOAuth.svelte +++ b/src/routes/console/project-[project]/auth/(providers)/mainOAuth.svelte @@ -5,9 +5,10 @@ import type { Provider } from '$lib/stores/oauth-providers'; import { sdk } from '$lib/stores/sdk'; import { onMount } from 'svelte'; - import { updateOAuth } from './updateOAuth'; + import { updateOAuth } from '../updateOAuth'; export let provider: Provider; + export let show = false; const projectId = $page.params.project; @@ -34,7 +35,7 @@ }; - + {provider.name} OAuth2 Settings

diff --git a/src/routes/console/project-[project]/auth/microsoftOAuth.svelte b/src/routes/console/project-[project]/auth/(providers)/microsoftOAuth.svelte similarity index 95% rename from src/routes/console/project-[project]/auth/microsoftOAuth.svelte rename to src/routes/console/project-[project]/auth/(providers)/microsoftOAuth.svelte index 266713e8e..7fd451af5 100644 --- a/src/routes/console/project-[project]/auth/microsoftOAuth.svelte +++ b/src/routes/console/project-[project]/auth/(providers)/microsoftOAuth.svelte @@ -5,9 +5,10 @@ import type { Provider } from '$lib/stores/oauth-providers'; import { sdk } from '$lib/stores/sdk'; import { onMount } from 'svelte'; - import { updateOAuth } from './updateOAuth'; + import { updateOAuth } from '../updateOAuth'; export let provider: Provider; + export let show = false; let appId: string = null; let enabled: boolean = null; @@ -37,7 +38,7 @@ clientSecret && tenantID ? JSON.stringify({ clientSecret, tenantID }) : provider.secret; - + {provider.name} OAuth2 Settings

diff --git a/src/routes/console/project-[project]/auth/oidcOAuth.svelte b/src/routes/console/project-[project]/auth/(providers)/oidcOAuth.svelte similarity index 96% rename from src/routes/console/project-[project]/auth/oidcOAuth.svelte rename to src/routes/console/project-[project]/auth/(providers)/oidcOAuth.svelte index 719759e14..f00f07e30 100644 --- a/src/routes/console/project-[project]/auth/oidcOAuth.svelte +++ b/src/routes/console/project-[project]/auth/(providers)/oidcOAuth.svelte @@ -5,11 +5,12 @@ import type { Provider } from '$lib/stores/oauth-providers'; import { sdk } from '$lib/stores/sdk'; import { onMount } from 'svelte'; - import { updateOAuth } from './updateOAuth'; + import { updateOAuth } from '../updateOAuth'; const projectId = $page.params.project; export let provider: Provider; + export let show = false; let appId: string = null; let enabled: boolean = null; @@ -60,7 +61,7 @@ : provider.secret; - + {provider.name} OAuth2 Settings

diff --git a/src/routes/console/project-[project]/auth/oktaOAuth.svelte b/src/routes/console/project-[project]/auth/(providers)/oktaOAuth.svelte similarity index 95% rename from src/routes/console/project-[project]/auth/oktaOAuth.svelte rename to src/routes/console/project-[project]/auth/(providers)/oktaOAuth.svelte index b9e640f8d..6e23ca60c 100644 --- a/src/routes/console/project-[project]/auth/oktaOAuth.svelte +++ b/src/routes/console/project-[project]/auth/(providers)/oktaOAuth.svelte @@ -5,11 +5,12 @@ import type { Provider } from '$lib/stores/oauth-providers'; import { sdk } from '$lib/stores/sdk'; import { onMount } from 'svelte'; - import { updateOAuth } from './updateOAuth'; + import { updateOAuth } from '../updateOAuth'; const projectId = $page.params.project; export let provider: Provider; + export let show = false; let appId: string = null; let enabled: boolean = null; @@ -42,7 +43,7 @@ : provider.secret; - + {provider.name} OAuth2 Settings

diff --git a/src/routes/console/project-[project]/auth/settings/+page.svelte b/src/routes/console/project-[project]/auth/settings/+page.svelte index fea2bad07..610733117 100644 --- a/src/routes/console/project-[project]/auth/settings/+page.svelte +++ b/src/routes/console/project-[project]/auth/settings/+page.svelte @@ -14,6 +14,7 @@ import { project } from '../../store'; const projectId = $page.params.project; + let showProvider = false; $: { authMethods.load($project); @@ -74,6 +75,7 @@ class="card u-flex u-flex-vertical u-cross-center u-width-full-line" on:click={() => { selectedProvider = provider; + showProvider = true; trackEvent(`click_select_provider`, { provider: provider.key.toLowerCase() }); @@ -99,9 +101,13 @@ {/if} -{#if selectedProvider} +{#if selectedProvider && showProvider} (selectedProvider = null)} /> + bind:show={showProvider} + on:close={() => { + selectedProvider = null; + showProvider = false; + }} /> {/if} diff --git a/src/routes/console/project-[project]/functions/function-[function]/+layout.svelte b/src/routes/console/project-[project]/functions/function-[function]/+layout.svelte index d693ef604..8ae5bc6d4 100644 --- a/src/routes/console/project-[project]/functions/function-[function]/+layout.svelte +++ b/src/routes/console/project-[project]/functions/function-[function]/+layout.svelte @@ -5,7 +5,7 @@ import { Dependencies } from '$lib/constants'; import { invalidate, goto } from '$app/navigation'; import { registerCommands } from '$lib/commandCenter'; - import { execute, func, showCreateDeployment } from './store'; + import { func, showCreateDeployment } from './store'; import { project } from '../../store'; import type { Models } from '@appwrite.io/console'; import Execute from './execute.svelte'; @@ -147,4 +147,4 @@ - + diff --git a/src/routes/console/project-[project]/functions/function-[function]/+page.svelte b/src/routes/console/project-[project]/functions/function-[function]/+page.svelte index 1462e407d..aa0c3c17b 100644 --- a/src/routes/console/project-[project]/functions/function-[function]/+page.svelte +++ b/src/routes/console/project-[project]/functions/function-[function]/+page.svelte @@ -25,7 +25,7 @@ TableRow, TableScroll } from '$lib/elements/table'; - import { deploymentList, execute, func, proxyRuleList } from './store'; + import { deploymentList, execute, func, proxyRuleList, showFunctionExecute } from './store'; import { Container, ContainerHeader } from '$lib/layout'; import { app } from '$lib/stores/app'; import { calculateSize, humanFileSize } from '$lib/helpers/sizeConvertion'; @@ -151,7 +151,10 @@ diff --git a/src/routes/console/project-[project]/functions/function-[function]/execute.svelte b/src/routes/console/project-[project]/functions/function-[function]/execute.svelte index 57d52063d..aea981107 100644 --- a/src/routes/console/project-[project]/functions/function-[function]/execute.svelte +++ b/src/routes/console/project-[project]/functions/function-[function]/execute.svelte @@ -15,9 +15,8 @@ import Label from '$lib/elements/forms/label.svelte'; import { addNotification } from '$lib/stores/notifications'; import { sdk } from '$lib/stores/sdk'; - import { ExecutionMethod, type Models } from '@appwrite.io/console'; - - export let selectedFunction: Models.Function = null; + import { showFunctionExecute, execute } from './store'; + import { ExecutionMethod } from '@appwrite.io/console'; let path = '/'; let method = ExecutionMethod.GET; @@ -34,13 +33,8 @@ { label: 'OPTIONS', value: ExecutionMethod.OPTIONS } ]; - let show = false; let submitting = false; - $: if (selectedFunction && !show) { - show = true; - } - const handleSubmit = async () => { submitting = true; try { @@ -51,7 +45,7 @@ } await sdk.forProject.functions.createExecution( - selectedFunction.$id, + $execute.$id, body, true, path, @@ -60,7 +54,7 @@ ); if (!$page.url?.toString()?.includes('/executions')) { await goto( - `${base}/console/project-${$page.params.project}/functions/function-${selectedFunction.$id}/executions` + `${base}/console/project-${$page.params.project}/functions/function-${$execute.$id}/executions` ); } invalidate(Dependencies.EXECUTIONS); @@ -79,8 +73,8 @@ }; function close() { - selectedFunction = null; - show = false; + $execute = null; + $showFunctionExecute = false; } afterNavigate(close); @@ -89,10 +83,9 @@

Manually execute your function. - {#if selectedFunction?.version !== 'v3'} + {#if $execute?.version !== 'v3'} Customizable execution data now available for functions v3.0 diff --git a/src/routes/console/project-[project]/functions/function-[function]/executions/+page.svelte b/src/routes/console/project-[project]/functions/function-[function]/executions/+page.svelte index 6db3add0d..0b14ea74f 100644 --- a/src/routes/console/project-[project]/functions/function-[function]/executions/+page.svelte +++ b/src/routes/console/project-[project]/functions/function-[function]/executions/+page.svelte @@ -19,19 +19,16 @@ import { log } from '$lib/stores/logs'; import { sdk } from '$lib/stores/sdk'; import { onMount } from 'svelte'; - import { func } from '../store'; + import { func, execute, showFunctionExecute } from '../store'; import type { Models } from '@appwrite.io/console'; import { organization } from '$lib/stores/organization'; import { getServiceLimit, showUsageRatesModal } from '$lib/stores/billing'; import { project } from '$routes/console/project-[project]/store'; import Create from '../create.svelte'; - import Execute from '../execute.svelte'; import { abbreviateNumber } from '$lib/helpers/numbers'; export let data; - let selectedFunction: Models.Function = null; - onMount(() => { return sdk.forConsole.client.subscribe('console', (response) => { if (response.events.includes('functions.*.executions.*')) { @@ -54,7 +51,10 @@ title="Executions" buttonText="Execute now" buttonEvent="execute_function" - buttonMethod={() => (selectedFunction = $func)}> + buttonMethod={() => { + $execute = $func; + $showFunctionExecute = true; + }}>

The {tier} plan has limits

    @@ -169,7 +169,3 @@ {/if} - - - - diff --git a/src/routes/console/project-[project]/functions/function-[function]/settings/executeFunction.svelte b/src/routes/console/project-[project]/functions/function-[function]/settings/executeFunction.svelte index 112bc2758..ba4d81ea3 100644 --- a/src/routes/console/project-[project]/functions/function-[function]/settings/executeFunction.svelte +++ b/src/routes/console/project-[project]/functions/function-[function]/settings/executeFunction.svelte @@ -2,7 +2,7 @@ import { CardGrid, Heading, SvgIcon } from '$lib/components'; import { Button } from '$lib/elements/forms'; import { toLocaleDateTime } from '$lib/helpers/date'; - import { execute, func } from '../store'; + import { execute, func, showFunctionExecute } from '../store'; @@ -26,6 +26,11 @@ - + diff --git a/src/routes/console/project-[project]/functions/function-[function]/store.ts b/src/routes/console/project-[project]/functions/function-[function]/store.ts index c08ec380a..0bc01a1d4 100644 --- a/src/routes/console/project-[project]/functions/function-[function]/store.ts +++ b/src/routes/console/project-[project]/functions/function-[function]/store.ts @@ -12,6 +12,8 @@ export const proxyRuleList = derived( ($page) => $page.data.proxyRuleList as Models.ProxyRuleList ); export const execute: Writable = writable(); +export const showFunctionExecute: Writable = writable(false); + export const repositories: Writable<{ search: string; installationId: string; diff --git a/src/routes/console/project-[project]/storage/bucket-[bucket]/+page.svelte b/src/routes/console/project-[project]/storage/bucket-[bucket]/+page.svelte index 4175ca7ce..648548e50 100644 --- a/src/routes/console/project-[project]/storage/bucket-[bucket]/+page.svelte +++ b/src/routes/console/project-[project]/storage/bucket-[bucket]/+page.svelte @@ -33,14 +33,19 @@ TableRowLink } from '$lib/elements/table'; import { toLocaleDate } from '$lib/helpers/date'; - import { bytesToSize, calculateSize } from '$lib/helpers/sizeConvertion'; + import { + bytesToSize, + calculateSize, + humanFileSize, + sizeToBytes + } from '$lib/helpers/sizeConvertion'; import { Container, ContainerHeader } from '$lib/layout'; import type { Models } from '@appwrite.io/console'; import { addNotification } from '$lib/stores/notifications'; import { uploader } from '$lib/stores/uploader'; import { wizard } from '$lib/stores/wizard'; import { tooltip } from '$lib/actions/tooltip'; - import { showUsageRatesModal } from '$lib/stores/billing'; + import { getServiceLimit, showUsageRatesModal } from '$lib/stores/billing'; import { sdk } from '$lib/stores/sdk.js'; import Create from './create-file/create.svelte'; import DeleteFile from './deleteFile.svelte'; @@ -81,6 +86,10 @@ trackError(error, Submit.FileDelete); } } + + $: maxFileSize = isCloud + ? humanFileSize(sizeToBytes(getServiceLimit('fileSize'), 'MB', 1000)) + : null; @@ -105,10 +114,12 @@ {#if tier === 'Starter'}

    - You are limited to {limit} GB of storage on the {tier} plan. + You are limited to {limit} GB of total storage and {Math.floor( + parseInt(maxFileSize.value) + )}{maxFileSize.unit} on the {tier} plan. - for addtional storage. + for additional storage.

    {:else}

    diff --git a/src/routes/console/wizard/cloudOrganizationChangeTier/confirmDetails.svelte b/src/routes/console/wizard/cloudOrganizationChangeTier/confirmDetails.svelte index aa1c4bf8b..cc487c579 100644 --- a/src/routes/console/wizard/cloudOrganizationChangeTier/confirmDetails.svelte +++ b/src/routes/console/wizard/cloudOrganizationChangeTier/confirmDetails.svelte @@ -2,7 +2,7 @@ import { Box, CreditCardBrandImage } from '$lib/components'; import { CouponInput } from '$lib/components/billing'; import { BillingPlan } from '$lib/constants'; - import { FormList, InputTextarea } from '$lib/elements/forms'; + import { FormList, InputSelect, InputTextarea } from '$lib/elements/forms'; import { toLocaleDate } from '$lib/helpers/date'; import { formatCurrency } from '$lib/helpers/numbers'; import { WizardStep } from '$lib/layout'; @@ -10,7 +10,12 @@ import { plansInfo } from '$lib/stores/billing'; import { organization } from '$lib/stores/organization'; import { sdk } from '$lib/stores/sdk'; - import { changeOrganizationFinalAction, changeOrganizationTier, isUpgrade } from './store'; + import { + changeOrganizationFinalAction, + changeOrganizationTier, + feedbackDowngradeOptions, + isUpgrade + } from './store'; const plan = $plansInfo.get($changeOrganizationTier.billingPlan); const collaboratorPrice = plan?.addons.member?.price ?? 0; @@ -28,7 +33,6 @@ status: null, credits: null }; - let comment: string = null; async function fetchCard() { try { @@ -51,16 +55,22 @@ Change confirmation - Your feedback is important to us and helps us improve the services Appwrite offer. If - there is a specific reason you chose to change your plan at this time, please let us - know. + Your feedback is important to us and helps us improve the services Appwrite offers. + Please let us know if there is a specific reason for changing your plan. + + placeholder="Enter feedback" + bind:value={$changeOrganizationTier.feedbackMessage} /> {:else} diff --git a/src/routes/console/wizard/cloudOrganizationChangeTier/store.ts b/src/routes/console/wizard/cloudOrganizationChangeTier/store.ts index f9f97b578..846dbabcc 100644 --- a/src/routes/console/wizard/cloudOrganizationChangeTier/store.ts +++ b/src/routes/console/wizard/cloudOrganizationChangeTier/store.ts @@ -23,6 +23,7 @@ export const changeOrganizationTier = writable<{ }; taxId?: string; feedbackMessage?: string; + feedbackDowngradeReason?: string; couponCode?: string; }>({ billingPlan: BillingPlan.PRO, @@ -32,3 +33,38 @@ export const changeOrganizationTier = writable<{ 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/vite.config.ts b/vite.config.ts index 58800bff3..de1c825dd 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -35,8 +35,10 @@ const testConfig = defineConfig({ globals: true, pool: 'threads', setupFiles: ['./tests/unit/setup.ts'], - deps: { - inline: ['@analytics/type-utils'] + server: { + deps: { + inline: ['@analytics/type-utils'] + } } } });