mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge branch 'main' of https://github.com/appwrite/console into 1.5.x
This commit is contained in:
@@ -3,7 +3,10 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="description" content="" />
|
||||
|
||||
<link rel="icon" type="image/svg+xml" href="/logos/appwrite-icon.svg" />
|
||||
<link rel="mask-icon" type="image/png" href="/logos/appwrite-icon.png" />
|
||||
|
||||
<link
|
||||
rel="preload"
|
||||
href="/fonts/inter/inter-v8-latin-600.woff2"
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { HeaderAlert } from '$lib/layout';
|
||||
import { orgMissingPaymentMethod } from '$routes/console/store';
|
||||
</script>
|
||||
|
||||
{#if ($orgMissingPaymentMethod.billingPlan === BillingPlan.PRO || $orgMissingPaymentMethod.billingPlan === BillingPlan.SCALE) && !$orgMissingPaymentMethod.paymentMethodId && !$orgMissingPaymentMethod.backupPaymentMethodId && !$page.url.pathname.includes('/console/account')}
|
||||
<HeaderAlert
|
||||
type="error"
|
||||
title={`Payment method required for ${$orgMissingPaymentMethod.name}`}>
|
||||
<svelte:fragment>
|
||||
Add a payment method to {$orgMissingPaymentMethod.name} to avoid service interruptions to
|
||||
your projects.
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="buttons">
|
||||
<Button
|
||||
secondary
|
||||
href={`/console/organization-${$orgMissingPaymentMethod.$id}/billing`}>
|
||||
Add payment method
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
</HeaderAlert>
|
||||
{/if}
|
||||
@@ -24,7 +24,7 @@
|
||||
{#if single}
|
||||
<article class="card u-grid u-cross-center u-width-full-line common-section">
|
||||
<div
|
||||
class="u-flex u-flex-vertical u-cross-center u-gap-24 u-width-full-line u-overflow-hidden">
|
||||
class="u-flex u-flex-vertical u-cross-center u-gap-24 u-width-full-line u-overflow-hidden u-padding-block-8">
|
||||
{#if !noMedia}
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { page } from '$app/stores';
|
||||
import { log } from '$lib/stores/logs';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import { activeHeaderAlert } from '$routes/console/store';
|
||||
|
||||
export let isOpen = false;
|
||||
export let showSideNavigation = false;
|
||||
@@ -47,9 +48,9 @@
|
||||
class:grid-with-side={showSideNavigation}
|
||||
class:is-open={isOpen}
|
||||
class:u-hide={$wizard.show || $log.show || $wizard.cover}
|
||||
class:is-fixed-layout={$$slots.alert}>
|
||||
{#if $$slots.alert}
|
||||
<slot name="alert" />
|
||||
class:is-fixed-layout={$activeHeaderAlert?.show}>
|
||||
{#if $activeHeaderAlert?.show}
|
||||
<svelte:component this={$activeHeaderAlert.component} />
|
||||
{/if}
|
||||
<header class="main-header u-padding-inline-end-0">
|
||||
<button
|
||||
|
||||
+18
-2
@@ -1,5 +1,5 @@
|
||||
import type { Client, Models } from '@appwrite.io/console';
|
||||
import type { Organization } from '../stores/organization';
|
||||
import type { Client, Models, Query } from '@appwrite.io/console';
|
||||
import type { Organization, OrganizationList } from '../stores/organization';
|
||||
import type { PaymentMethod } from '@stripe/stripe-js';
|
||||
import type { Tier } from '$lib/stores/billing';
|
||||
|
||||
@@ -274,6 +274,22 @@ export class Billing {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
async listOrganization(queries: Query[] = []): Promise<OrganizationList> {
|
||||
const path = `/organizations`;
|
||||
const params = {
|
||||
queries
|
||||
};
|
||||
const uri = new URL(this.client.config.endpoint + path);
|
||||
return await this.client.call(
|
||||
'GET',
|
||||
uri,
|
||||
{
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
async createOrganization(
|
||||
organizationId: string,
|
||||
name: string,
|
||||
|
||||
@@ -11,9 +11,10 @@ import PaymentAuthRequired from '$lib/components/billing/alerts/paymentAuthRequi
|
||||
import { addNotification, notifications } from './notifications';
|
||||
import { goto } from '$app/navigation';
|
||||
import { base } from '$app/paths';
|
||||
import { activeHeaderAlert } from '$routes/console/store';
|
||||
import { activeHeaderAlert, orgMissingPaymentMethod } from '$routes/console/store';
|
||||
import MarkedForDeletion from '$lib/components/billing/alerts/markedForDeletion.svelte';
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import MissingPaymentMethod from '$lib/components/billing/alerts/missingPaymentMethod.svelte';
|
||||
|
||||
export type Tier = 'tier-0' | 'tier-1' | 'tier-2';
|
||||
|
||||
@@ -265,3 +266,20 @@ export function checkForMarkedForDeletion(org: Organization) {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkForMissingPaymentMethod() {
|
||||
const orgs = await sdk.forConsole.billing.listOrganization([
|
||||
Query.notEqual('billingPlan', BillingPlan.STARTER),
|
||||
Query.isNull('paymentMethodId'),
|
||||
Query.isNull('backupPaymentMethodId')
|
||||
]);
|
||||
if (orgs?.total) {
|
||||
orgMissingPaymentMethod.set(orgs.teams[0]);
|
||||
headerAlert.add({
|
||||
id: 'missingPaymentMethod',
|
||||
component: MissingPaymentMethod,
|
||||
show: true,
|
||||
importance: 8
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,11 @@ export type Organization = Models.Team<Record<string, unknown>> & {
|
||||
billingPlanDowngrade?: string;
|
||||
};
|
||||
|
||||
export type OrganizationList = {
|
||||
teams: Organization[];
|
||||
total: number;
|
||||
};
|
||||
|
||||
export type BillingLimits = {
|
||||
bandwidth: number;
|
||||
documents: number;
|
||||
|
||||
@@ -19,7 +19,12 @@ export async function initializeStripe() {
|
||||
isStripeInitialized.set(true);
|
||||
|
||||
const methods = await sdk.forConsole.billing.listPaymentMethods();
|
||||
clientSecret = methods.paymentMethods[0]?.clientSecret;
|
||||
|
||||
// Get the client secret from empty payment method if available
|
||||
clientSecret = methods.paymentMethods?.filter(
|
||||
(method) => !!method?.clientSecret && !method?.providerMethodId
|
||||
)[0]?.clientSecret;
|
||||
|
||||
// If there is no payment method, create an empty one and get the client secret
|
||||
if (!clientSecret) {
|
||||
paymentMethod = await sdk.forConsole.billing.createPaymentMethod();
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
checkPaymentAuthorizationRequired,
|
||||
calculateTrialDay,
|
||||
paymentExpired,
|
||||
checkForMarkedForDeletion
|
||||
checkForMarkedForDeletion,
|
||||
checkForMissingPaymentMethod
|
||||
} from '$lib/stores/billing';
|
||||
import { goto } from '$app/navigation';
|
||||
import { CommandCenter, registerCommands, registerSearchers } from '$lib/commandCenter';
|
||||
@@ -236,6 +237,7 @@
|
||||
|
||||
if (isCloud && hasStripePublicKey) {
|
||||
$stripe = await loadStripe(VARS.STRIPE_PUBLIC_KEY);
|
||||
await checkForMissingPaymentMethod();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -288,11 +290,6 @@
|
||||
!$page.url.pathname.includes('/console/account') &&
|
||||
!$page.url.pathname.includes('/console/card') &&
|
||||
!$page.url.pathname.includes('/console/onboarding')}>
|
||||
<svelte:fragment slot="alert">
|
||||
{#if $activeHeaderAlert?.show}
|
||||
<svelte:component this={$activeHeaderAlert.component} />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<Header slot="header" />
|
||||
<SideNavigation slot="side" bind:isOpen />
|
||||
<slot />
|
||||
|
||||
@@ -20,7 +20,7 @@ export const load: LayoutLoad = async ({ params, depends }) => {
|
||||
if (isCloud) {
|
||||
await failedInvoice.load(params.organization);
|
||||
|
||||
if (!get(failedInvoice)) {
|
||||
if (get(failedInvoice)) {
|
||||
headerAlert.add({
|
||||
show: true,
|
||||
component: ProjectsAtRisk,
|
||||
|
||||
@@ -18,15 +18,18 @@
|
||||
let name: string;
|
||||
let showCustomId = false;
|
||||
let error: string;
|
||||
let disabled: boolean = false;
|
||||
|
||||
async function create() {
|
||||
try {
|
||||
disabled = true;
|
||||
const project = await sdk.forConsole.projects.create(
|
||||
id ?? ID.unique(),
|
||||
name,
|
||||
teamId,
|
||||
Region.Default
|
||||
);
|
||||
show = false;
|
||||
dispatch('created', project);
|
||||
trackEvent(Submit.ProjectCreate, {
|
||||
customId: !!id,
|
||||
@@ -40,6 +43,7 @@
|
||||
} catch (e) {
|
||||
error = e.message;
|
||||
trackError(e, Submit.ProjectCreate);
|
||||
disabled = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -61,6 +65,6 @@
|
||||
</FormList>
|
||||
<svelte:fragment slot="footer">
|
||||
<Button secondary on:click={() => (show = false)}>Cancel</Button>
|
||||
<Button submit>Create</Button>
|
||||
<Button submit {disabled}>Create</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
|
||||
+2
-2
@@ -58,9 +58,9 @@
|
||||
</Button>
|
||||
</div>
|
||||
{:else}
|
||||
<ul class="u-grid u-gap-8">
|
||||
<ul class="u-grid u-gap-4">
|
||||
{#each [...formValues[attribute.key].keys()] as index}
|
||||
<li class="form-item is-multiple">
|
||||
<li class="form-item is-multiple u-gap-8">
|
||||
<div class="form-item-part u-stretch">
|
||||
<Attribute
|
||||
{attribute}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import ChangeOrganizationTierCloud from '$routes/console/changeOrganizationTierCloud.svelte';
|
||||
import { SMTPSecure } from '@appwrite.io/console';
|
||||
import InputSelect from '$lib/elements/forms/inputSelect.svelte';
|
||||
|
||||
let enabled = false;
|
||||
let senderName: string;
|
||||
@@ -33,7 +34,13 @@
|
||||
let port: number;
|
||||
let username: string;
|
||||
let password: string;
|
||||
let secure = false;
|
||||
let secure: string;
|
||||
|
||||
const options = [
|
||||
{ value: 'tls', label: 'TLS' },
|
||||
{ value: 'ssl', label: 'SSL' },
|
||||
{ value: '', label: 'None' }
|
||||
];
|
||||
|
||||
onMount(() => {
|
||||
enabled = $project.smtpEnabled ?? false;
|
||||
@@ -44,7 +51,7 @@
|
||||
port = $project.smtpPort;
|
||||
username = $project.smtpUsername;
|
||||
password = $project.smtpPassword;
|
||||
secure = $project.smtpSecure === 'tls';
|
||||
secure = $project.smtpSecure === 'tls' ? 'tls' : $project.smtpSecure === 'ssl' ? 'ssl' : '';
|
||||
});
|
||||
|
||||
async function updateSmtp() {
|
||||
@@ -98,7 +105,7 @@
|
||||
port: $project.smtpPort,
|
||||
username: $project.smtpUsername,
|
||||
password: $project.smtpPassword,
|
||||
secure: $project.smtpSecure === 'tls'
|
||||
secure: $project.smtpSecure
|
||||
}
|
||||
);
|
||||
|
||||
@@ -110,7 +117,7 @@
|
||||
port = undefined;
|
||||
username = undefined;
|
||||
password = undefined;
|
||||
secure = false;
|
||||
secure = undefined;
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -190,10 +197,12 @@
|
||||
label="Password"
|
||||
bind:value={password}
|
||||
placeholder="Enter password" />
|
||||
|
||||
<InputChoice bind:value={secure} id="tls" label="TLS secure protocol">
|
||||
Enable if TLS is supported on your SMTP server.
|
||||
</InputChoice>
|
||||
<InputSelect
|
||||
id="tls"
|
||||
label="Secure protocol"
|
||||
placeholder="Select protocol"
|
||||
bind:value={secure}
|
||||
{options} />
|
||||
{/if}
|
||||
</FormList>
|
||||
{/if}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { page } from '$app/stores';
|
||||
import type { HeaderAlert } from '$lib/stores/headerAlert';
|
||||
import type { Organization } from '$lib/stores/organization';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { derived, writable } from 'svelte/store';
|
||||
|
||||
@@ -10,3 +11,4 @@ export const consoleVariables = derived(
|
||||
);
|
||||
|
||||
export const activeHeaderAlert = writable<HeaderAlert>(null);
|
||||
export const orgMissingPaymentMethod = writable<Organization>(null);
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 343 B |
Reference in New Issue
Block a user