Merge pull request #953 from appwrite/sync-main-to-cloud

chore: sync main with cloud
This commit is contained in:
Torsten Dittmann
2024-03-11 11:01:38 +01:00
committed by GitHub
37 changed files with 317 additions and 106 deletions
+1 -1
View File
@@ -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
+1
View File
@@ -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',
@@ -0,0 +1,26 @@
<script lang="ts">
import { page } from '$app/stores';
import { Button } from '$lib/elements/forms';
import { HeaderAlert } from '$lib/layout';
import { paymentMissingMandate } from '$lib/stores/billing';
import { organization } from '$lib/stores/organization';
import { sdk } from '$lib/stores/sdk';
import { confirmSetup } from '$lib/stores/stripe';
async function verifyPaymentMethod() {
const method = await sdk.forConsole.billing.setupPaymentMandate(
$organization.$id,
$paymentMissingMandate.$id
);
await confirmSetup(method.clientSecret, method.$id);
}
</script>
{#if $paymentMissingMandate && $paymentMissingMandate.country === 'in' && $paymentMissingMandate.mandateId === null && !$page.url.pathname.includes('/console/account')}
<HeaderAlert title="Authorization required" type="info">
The payment method for {$organization.name} needs to be verified.
<svelte:fragment slot="buttons">
<Button secondary on:click={verifyPaymentMethod}>Verify payment method</Button>
</svelte:fragment>
</HeaderAlert>
{/if}
+12 -10
View File
@@ -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<Map<string, Permission>>;
@@ -77,13 +79,13 @@
bind:value={search} />
{#if results?.teams?.length}
<div class="table-wrapper">
<table class="table is-table-layout-auto is-remove-outer-styles">
<tbody class="table-tbody">
<Table noStyles isAutoLayout tag="table">
<TableBody>
{#each results.teams as team (team.$id)}
{@const role = `team:${team.$id}`}
{@const exists = $groups.has(role)}
<tr class="table-row">
<td class="table-col" data-title="Enabled" style="--p-col-width:40">
<TableRow>
<TableCell title="Enabled" width={40}>
<input
id={team.$id}
type="checkbox"
@@ -92,8 +94,8 @@
checked={exists || selected.has(role)}
disabled={exists}
on:change={(event) => onSelection(event, role)} />
</td>
<td class="table-col" data-title="Team">
</TableCell>
<TableCell title="Team">
<label class="u-flex u-cross-center u-gap-8" for={team.$id}>
<AvatarInitials size={32} name={team.name} />
<div class="u-line-height-1-5">
@@ -101,11 +103,11 @@
<div class="u-x-small">{team.$id}</div>
</div>
</label>
</td>
</tr>
</TableCell>
</TableRow>
{/each}
</tbody>
</table>
</TableBody>
</Table>
</div>
<div class="u-flex u-margin-block-start-32 u-main-space-between">
<p class="text">Total results: {results?.total}</p>
+6 -2
View File
@@ -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;
</script>
<div
<svelte:element
this={tag}
class="table is-selected-columns-mobile"
class:is-table-layout-auto={isAutoLayout}
class:u-margin-block-start-32={!noMargin}
class:is-remove-outer-styles={noStyles}
class:is-table-row-medium-size={dense}
@@ -16,4 +20,4 @@
role="table"
data-private>
<slot />
</div>
</svelte:element>
@@ -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<HTMLDivElement, unknown> = (node) => {
@@ -45,6 +47,7 @@
<div class="table-wrapper" use:hasOverflow={(v) => (isOverflowing = v)}>
<table
class="table"
class:is-remove-outer-styles={noStyles}
class:is-sticky-scroll={isSticky && isOverflowing}
class:is-table-row-medium-size={dense}
{style}
+4 -5
View File
@@ -120,19 +120,18 @@
<DropList bind:show={showDropdown} width="16">
{#if hasProjectLimitation}
<Pill button on:click={() => (showDropdown = !showDropdown)}>
<span class="icon-info" />{total}/{limit}
{title} used
<span class="icon-info" />{total}/{limit} created
</Pill>
{:else}
<Pill button on:click={() => (showDropdown = !showDropdown)}>
<span class="icon-info" />{title} limited
<span class="icon-info" />Limits applied
</Pill>
{/if}
<svelte:fragment slot="list">
<slot name="tooltip" {limit} {tier} {title} {upgradeMethod} {hasUsageFees}>
{#if hasProjectLimitation}
<p class="text">
Your are limited to {limit}
You are limited to {limit}
{title.toLocaleLowerCase()} per project on the {tier} plan.
{#if $organization?.billingPlan === BillingPlan.STARTER}<Button
link
@@ -142,7 +141,7 @@
from: 'button',
source: 'resource_limit_tag'
})}>Upgrade</Button>
for addtional {title.toLocaleLowerCase()}.
for additional {title.toLocaleLowerCase()}.
{/if}
</p>
{:else if hasUsageFees}
+21
View File
@@ -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<PaymentMethodData> {
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<AddressesList> {
const path = `/account/billing-addresses`;
const params = {
+27 -2
View File
@@ -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<PaymentMethodData>(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),
+9 -9
View File
@@ -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;
+42 -1
View File
@@ -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<Stripe>();
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',
+2
View File
@@ -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);
}
});
@@ -82,7 +82,7 @@
<Pill>FREE</Pill>
</div>
{/if}
{#if organization?.billingTrialStartDate && $daysLeftInTrial > 0}
{#if organization?.billingTrialStartDate && $daysLeftInTrial > 0 && organization.billingPlan !== BillingPlan.STARTER}
<div
class="u-flex u-cross-center"
use:tooltip={{
@@ -14,6 +14,7 @@
changeOrganizationFinalAction,
changeOrganizationTier,
changeTierSteps,
feedbackDowngradeOptions,
isUpgrade
} from './wizard/cloudOrganizationChangeTier/store';
import { goto, invalidate } from '$app/navigation';
@@ -24,7 +25,7 @@
import { wizard } from '$lib/stores/wizard';
import { tierToPlan } from '$lib/stores/billing';
import { user } from '$lib/stores/user';
import { feedback } from '$lib/stores/feedback';
import { VARS } from '$lib/system';
const dispatch = createEventDispatcher();
@@ -42,12 +43,25 @@
$changeOrganizationTier.paymentMethodId,
$changeOrganizationTier.billingAddressId
);
feedback.submitFeedback(
'downgrade',
$changeOrganizationTier.feedbackMessage,
$user?.name ?? '',
$user.email
);
await fetch(`${VARS.GROWTH_ENDPOINT}/feedback/billing`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
from: tierToPlan($organization.billingPlan).name,
to: tierToPlan($changeOrganizationTier.billingPlan).name,
email: $user.email,
reason: feedbackDowngradeOptions.find(
(option) =>
option.value === $changeOrganizationTier.feedbackDowngradeReason
)?.label,
orgId: $organization.$id,
userId: $user.$id,
message: $changeOrganizationTier?.feedbackMessage ?? ''
})
});
addNotification({
type: 'success',
@@ -65,7 +65,7 @@
</p>
<svelte:fragment slot="aside">
{#if invoiceList.total - 1 > 0}
<TableScroll noMargin transparent>
<TableScroll noMargin transparent noStyles>
<TableHeader>
<TableCellHead width={100}>Due Date</TableCellHead>
<TableCellHead width={80}>Status</TableCellHead>
@@ -94,7 +94,7 @@
<TableCellText title="due">
{formatCurrency(invoice.amount)}
</TableCellText>
<TableCell showOverflow>
<TableCell showOverflow right>
<DropList
bind:show={showDropdown[i]}
placement="bottom-start"
@@ -94,7 +94,7 @@
{#if isCloud && $organization?.billingPlan === BillingPlan.STARTER}
<Pill>FREE</Pill>
{/if}
{#if isCloud && $organization?.billingTrialStartDate && $daysLeftInTrial > 0}
{#if isCloud && $organization?.billingTrialStartDate && $daysLeftInTrial > 0 && $organization.billingPlan !== BillingPlan.STARTER}
<div
class="u-flex u-cross-center"
use:tooltip={{
@@ -225,7 +225,7 @@
{@const max = getServiceLimit('executions', tier)}
<ProgressBarBig
currentUnit="Executions"
currentValue={current.toString()}
currentValue={formatNum(current)}
maxUnit="Executions"
maxValue={formatNum(max)}
progressValue={current}
@@ -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;
@@ -37,7 +38,7 @@
$: secret = keyID && teamID && p8 ? JSON.stringify({ keyID, teamID, p8 }) : provider.secret;
</script>
<Modal {error} onSubmit={update} size="big" show on:close>
<Modal {error} onSubmit={update} size="big" bind:show on:close>
<svelte:fragment slot="title">{provider.name} OAuth2 Settings</svelte:fragment>
<FormList>
<p>
@@ -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;
</script>
<Modal {error} size="big" show onSubmit={update} on:close>
<Modal {error} size="big" bind:show onSubmit={update} on:close>
<svelte:fragment slot="title">{provider.name} OAuth2 Settings</svelte:fragment>
<FormList>
<p>
@@ -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;
</script>
<Modal {error} size="big" show onSubmit={update} on:close>
<Modal {error} size="big" bind:show onSubmit={update} on:close>
<svelte:fragment slot="title">{provider.name} OAuth2 Settings</svelte:fragment>
<FormList>
<p>
@@ -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;
</script>
<Modal {error} size="big" show onSubmit={update} on:close>
<Modal {error} size="big" bind:show onSubmit={update} on:close>
<svelte:fragment slot="title">{provider.name} OAuth2 Settings</svelte:fragment>
<FormList>
<p>
@@ -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 @@
};
</script>
<Modal {error} size="big" show onSubmit={update} on:close>
<Modal {error} size="big" bind:show onSubmit={update} on:close>
<svelte:fragment slot="title">{provider.name} OAuth2 Settings</svelte:fragment>
<FormList>
<p>
@@ -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 @@
};
</script>
<Modal {error} size="big" show onSubmit={update} on:close>
<Modal {error} size="big" bind:show onSubmit={update} on:close>
<svelte:fragment slot="title">{provider.name} OAuth2 Settings</svelte:fragment>
<FormList>
<p>
@@ -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;
</script>
<Modal {error} onSubmit={update} size="big" show on:close>
<Modal {error} onSubmit={update} size="big" bind:show on:close>
<svelte:fragment slot="title">{provider.name} OAuth2 Settings</svelte:fragment>
<FormList>
<p>
@@ -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;
</script>
<Modal {error} onSubmit={update} size="big" show on:close>
<Modal {error} onSubmit={update} size="big" bind:show on:close>
<svelte:fragment slot="title">{provider.name} OAuth2 Settings</svelte:fragment>
<FormList>
<p>
@@ -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;
</script>
<Modal {error} onSubmit={update} size="big" show on:close>
<Modal {error} onSubmit={update} size="big" bind:show on:close>
<svelte:fragment slot="title">{provider.name} OAuth2 Settings</svelte:fragment>
<FormList>
<p>
@@ -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 @@
</Container>
{/if}
{#if selectedProvider}
{#if selectedProvider && showProvider}
<svelte:component
this={selectedProvider.component}
bind:provider={selectedProvider}
on:close={() => (selectedProvider = null)} />
bind:show={showProvider}
on:close={() => {
selectedProvider = null;
showProvider = false;
}} />
{/if}
@@ -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 @@
<slot />
<Execute selectedFunction={$execute} />
<Execute />
@@ -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 @@
<Button
secondary
on:click={() => ($execute = $func)}
on:click={() => {
$execute = $func;
$showFunctionExecute = true;
}}
disabled={isCloud && $readOnly && !GRACE_PERIOD_OVERRIDE}>
Execute now
</Button>
@@ -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 @@
<Modal
title="Execute function"
headerDivider={false}
bind:show
bind:show={$showFunctionExecute}
size="big"
onSubmit={handleSubmit}
on:close={close}
bind:error>
<p class="text">
Manually execute your function. <a
@@ -105,7 +98,7 @@
</p>
<FormList>
{#if selectedFunction?.version !== 'v3'}
{#if $execute?.version !== 'v3'}
<Alert type="info">
<svelte:fragment slot="title">
Customizable execution data now available for functions v3.0
@@ -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;
}}>
<svelte:fragment slot="tooltip" let:tier let:limit let:upgradeMethod>
<p class="u-bold">The {tier} plan has limits</p>
<ul>
@@ -169,7 +169,3 @@
</EmptySearch>
{/if}
</Container>
<!-- <CreateDeployment bind:showCreate /> -->
<Execute {selectedFunction} />
@@ -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';
</script>
<CardGrid>
@@ -26,6 +26,11 @@
</svelte:fragment>
<svelte:fragment slot="actions">
<Button secondary on:click={() => ($execute = $func)}>Execute now</Button>
<Button
secondary
on:click={() => {
$execute = $func;
$showFunctionExecute = true;
}}>Execute now</Button>
</svelte:fragment>
</CardGrid>
@@ -12,6 +12,8 @@ export const proxyRuleList = derived(
($page) => $page.data.proxyRuleList as Models.ProxyRuleList
);
export const execute: Writable<Models.Function> = writable();
export const showFunctionExecute: Writable<boolean> = writable(false);
export const repositories: Writable<{
search: string;
installationId: string;
@@ -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;
</script>
<Container>
@@ -105,10 +114,12 @@
<svelte:fragment slot="tooltip" let:limit let:tier let:upgradeMethod>
{#if tier === 'Starter'}
<p class="text">
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.
<button class="link" type="button" on:click|preventDefault={upgradeMethod}
>Upgrade</button>
for addtional storage.
for additional storage.
</p>
{:else}
<p class="text">
@@ -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 @@
<WizardStep>
<svelte:fragment slot="title">Change confirmation</svelte:fragment>
<svelte:fragment slot="subtitle">
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.
</svelte:fragment>
<FormList>
<InputSelect
id="reason"
label="What made you decide to change your plan?*"
placeholder="Select one"
required
options={feedbackDowngradeOptions}
bind:value={$changeOrganizationTier.feedbackDowngradeReason} />
<InputTextarea
id="comment"
label="Your feedback here"
placeholder="This is my reason for downgrading..."
bind:value={comment} />
placeholder="Enter feedback"
bind:value={$changeOrganizationTier.feedbackMessage} />
</FormList>
</WizardStep>
{:else}
@@ -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'
}
];
+4 -2
View File
@@ -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']
}
}
}
});