mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge pull request #2706 from appwrite/improve-payment-modal
This commit is contained in:
@@ -1,8 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { FakeModal } from '$lib/components';
|
||||
import { InputText, Button } from '$lib/elements/forms';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { initializeStripe, setPaymentMethod, submitStripeCard } from '$lib/stores/stripe';
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import {
|
||||
initializeStripe,
|
||||
setPaymentMethod,
|
||||
submitStripeCard,
|
||||
unmountPaymentElement
|
||||
} from '$lib/stores/stripe';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
@@ -10,16 +15,16 @@
|
||||
import { Spinner } from '@appwrite.io/pink-svelte';
|
||||
import type { PaymentMethod } from '@stripe/stripe-js';
|
||||
import StatePicker from './statePicker.svelte';
|
||||
import type { PaymentMethodData } from '$lib/sdk/billing';
|
||||
|
||||
export let show = false;
|
||||
export let onCardSubmit: ((card: PaymentMethodData) => void) | null = null;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let name: string;
|
||||
let error: string;
|
||||
let modal: FakeModal;
|
||||
let showState: boolean = false;
|
||||
let name: string;
|
||||
let state: string = '';
|
||||
let error: string = null;
|
||||
let showState: boolean = false;
|
||||
let paymentMethod: PaymentMethod | null = null;
|
||||
|
||||
async function handleSubmit() {
|
||||
@@ -32,7 +37,7 @@
|
||||
const card = await setPaymentMethod(paymentMethod.id, name, state);
|
||||
modal.closeModal();
|
||||
await invalidate(Dependencies.PAYMENT_METHODS);
|
||||
dispatch('submit', card);
|
||||
onCardSubmit?.(card);
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'A new payment method has been added to your account'
|
||||
@@ -50,7 +55,7 @@
|
||||
}
|
||||
modal.closeModal();
|
||||
await invalidate(Dependencies.PAYMENT_METHODS);
|
||||
dispatch('submit', card);
|
||||
onCardSubmit?.(card as PaymentMethodData);
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'A new payment method has been added to your account'
|
||||
@@ -89,6 +94,8 @@
|
||||
};
|
||||
});
|
||||
|
||||
onDestroy(unmountPaymentElement);
|
||||
|
||||
$: if (element) {
|
||||
observer.observe(element, { childList: true });
|
||||
}
|
||||
@@ -99,7 +106,8 @@
|
||||
bind:show
|
||||
title="Add payment method"
|
||||
bind:error
|
||||
onSubmit={handleSubmit}>
|
||||
onSubmit={handleSubmit}
|
||||
skipEnterOnBackdrop={showState}>
|
||||
<slot />
|
||||
{#if showState}
|
||||
<StatePicker card={paymentMethod} bind:state />
|
||||
|
||||
@@ -18,14 +18,14 @@
|
||||
let showTaxId = false;
|
||||
let showPaymentModal = false;
|
||||
|
||||
async function cardSaved(event: CustomEvent<PaymentMethodData>) {
|
||||
value = event.detail.$id;
|
||||
async function cardSaved(card: PaymentMethodData) {
|
||||
value = card.$id;
|
||||
|
||||
if (value) {
|
||||
methods = {
|
||||
...methods,
|
||||
total: methods.total + 1,
|
||||
paymentMethods: [...methods.paymentMethods, event.detail]
|
||||
paymentMethods: [...methods.paymentMethods, card]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
</Layout.Stack>
|
||||
|
||||
{#if showPaymentModal && isCloud && hasStripePublicKey}
|
||||
<PaymentModal bind:show={showPaymentModal} on:submit={cardSaved}>
|
||||
<PaymentModal bind:show={showPaymentModal} onCardSubmit={cardSaved}>
|
||||
<svelte:fragment slot="end">
|
||||
<Selector.Checkbox
|
||||
id="taxIdCheck"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { Alert } from '@appwrite.io/pink-svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { clickOnEnter } from '$lib/helpers/a11y';
|
||||
import Form from '$lib/elements/forms/form.svelte';
|
||||
import { Click, trackEvent } from '$lib/actions/analytics';
|
||||
import { clickOnEnter } from '$lib/helpers/a11y';
|
||||
|
||||
export let title = '';
|
||||
export let show = false;
|
||||
export let size: 'small' | 'big' = 'big';
|
||||
export let icon: string = null;
|
||||
@@ -15,12 +15,14 @@
|
||||
export let onSubmit: (e: SubmitEvent) => Promise<void> | void = function () {
|
||||
return;
|
||||
};
|
||||
export let title = '';
|
||||
|
||||
/**
|
||||
* needed when using `StatePicker`
|
||||
*/
|
||||
export let skipEnterOnBackdrop = false;
|
||||
|
||||
let backdrop: HTMLDivElement;
|
||||
|
||||
onMount(async () => {});
|
||||
|
||||
function handleBLur(event: MouseEvent) {
|
||||
if (event.target === backdrop) {
|
||||
trackEvent(Click.ModalCloseClick, {
|
||||
@@ -60,12 +62,12 @@
|
||||
<svelte:window on:keydown={handleKeydown} />
|
||||
|
||||
{#if show}
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div
|
||||
bind:this={backdrop}
|
||||
onclick={handleBLur}
|
||||
class="payment-modal-backdrop"
|
||||
on:keyup={clickOnEnter}
|
||||
on:click={handleBLur}
|
||||
bind:this={backdrop}>
|
||||
onkeyup={skipEnterOnBackdrop ? undefined : clickOnEnter}>
|
||||
<div
|
||||
class="modal"
|
||||
class:is-small={size === 'small'}
|
||||
@@ -99,11 +101,12 @@
|
||||
style="--button-size:1.5rem;"
|
||||
aria-label="Close Modal"
|
||||
title="Close Modal"
|
||||
on:click={() =>
|
||||
onclick={() => {
|
||||
closeModal();
|
||||
trackEvent(Click.ModalCloseClick, {
|
||||
from: 'button'
|
||||
})}
|
||||
on:click={closeModal}>
|
||||
});
|
||||
}}>
|
||||
<span class="icon-x" aria-hidden="true"></span>
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
@@ -26,6 +26,10 @@ export const isStripeInitialized = writable(false);
|
||||
|
||||
export async function initializeStripe(node: HTMLElement) {
|
||||
if (!get(stripe)) return;
|
||||
|
||||
// cleanup any existing state
|
||||
await unmountPaymentElement();
|
||||
|
||||
isStripeInitialized.set(true);
|
||||
|
||||
const methods = await sdk.forConsole.billing.listPaymentMethods();
|
||||
@@ -54,10 +58,20 @@ export async function initializeStripe(node: HTMLElement) {
|
||||
|
||||
export async function unmountPaymentElement() {
|
||||
isStripeInitialized.set(false);
|
||||
paymentElement?.unmount();
|
||||
|
||||
if (paymentElement) {
|
||||
try {
|
||||
paymentElement.unmount();
|
||||
paymentElement.destroy();
|
||||
} catch (e) {
|
||||
console.debug('Payment element cleanup:', e.message);
|
||||
}
|
||||
}
|
||||
|
||||
elements = null;
|
||||
clientSecret = null;
|
||||
paymentMethod = null;
|
||||
elements = null;
|
||||
paymentElement = null;
|
||||
}
|
||||
|
||||
export async function submitStripeCard(name: string, organizationId?: string) {
|
||||
|
||||
@@ -308,11 +308,11 @@
|
||||
{#if showPayment && isCloud && hasStripePublicKey}
|
||||
<PaymentModal
|
||||
bind:show={showPayment}
|
||||
on:submit={(e) => {
|
||||
onCardSubmit={(card) => {
|
||||
if (isSelectedBackup) {
|
||||
addBackupPaymentMethod(e.detail.$id);
|
||||
addBackupPaymentMethod(card.$id);
|
||||
} else {
|
||||
addPaymentMethod(e.detail.$id);
|
||||
addPaymentMethod(card.$id);
|
||||
}
|
||||
}} />
|
||||
{/if}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
export let methods: PaymentList;
|
||||
|
||||
let name: string;
|
||||
let error: string;
|
||||
let error: string = null;
|
||||
let selectedPaymentMethodId: string;
|
||||
let showState: boolean = false;
|
||||
let state: string = '';
|
||||
|
||||
@@ -147,7 +147,6 @@
|
||||
onSubmit={handleSubmit}
|
||||
size="big"
|
||||
title="Retry payment">
|
||||
<!-- TODO: format currency -->
|
||||
<p class="text">
|
||||
Your payment of <span class="inline-tag">{formatCurrency(invoice.grossAmount)}</span> due on {toLocaleDate(
|
||||
invoice.dueAt
|
||||
|
||||
Reference in New Issue
Block a user