mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge pull request #953 from appwrite/sync-main-to-cloud
chore: sync main with cloud
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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}
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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}
|
||||
|
||||
+3
-2
@@ -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>
|
||||
+3
-2
@@ -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>
|
||||
+3
-2
@@ -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>
|
||||
+3
-2
@@ -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>
|
||||
+3
-2
@@ -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>
|
||||
+3
-2
@@ -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>
|
||||
+3
-2
@@ -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>
|
||||
+3
-2
@@ -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>
|
||||
+3
-2
@@ -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
|
||||
|
||||
+5
-9
@@ -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} />
|
||||
|
||||
+7
-2
@@ -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
@@ -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']
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user