mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
fix and expand paymentBoxes
This commit is contained in:
@@ -1,12 +1,18 @@
|
||||
<script lang="ts">
|
||||
import { FormList, InputText } from '$lib/elements/forms';
|
||||
import { FormList, InputChoice, InputText } from '$lib/elements/forms';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { CreditCardBrandImage, RadioBoxes } from '..';
|
||||
import { unmountPaymentElement } from '$lib/stores/stripe';
|
||||
import { Pill } from '$lib/elements';
|
||||
|
||||
export let methods: Record<string, unknown>[];
|
||||
export let group: string;
|
||||
export let name: string;
|
||||
export let defaultMethod: string = null;
|
||||
export let backupMethod: string = null;
|
||||
export let disabledCondition: string = null;
|
||||
export let setAsDefault = false;
|
||||
export let showSetAsDefault = false;
|
||||
|
||||
let element: HTMLDivElement;
|
||||
let loader: HTMLDivElement;
|
||||
@@ -42,13 +48,36 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<RadioBoxes elements={methods} total={methods?.length} variableName="$id" name="payment" bind:group>
|
||||
<RadioBoxes
|
||||
elements={methods}
|
||||
total={methods?.length}
|
||||
variableName="$id"
|
||||
name="payment"
|
||||
bind:group
|
||||
{disabledCondition}>
|
||||
<svelte:fragment slot="element" let:element>
|
||||
<slot {element}>
|
||||
<span class="u-flex u-cross-center u-gap-8" style="padding-inline:0.25rem">
|
||||
<span>
|
||||
<span class="u-capitalize">{element.brand}</span> ending in {element.last4}</span>
|
||||
<CreditCardBrandImage brand={element.brand?.toString()} />
|
||||
<span class="u-flex u-gap-16 u-flex-vertical">
|
||||
<span class="u-flex u-gap-16">
|
||||
<span class="u-flex u-cross-center u-gap-8" style="padding-inline:0.25rem">
|
||||
<span>
|
||||
<span class="u-capitalize">{element.brand}</span> ending in {element.last4}</span>
|
||||
<CreditCardBrandImage brand={element.brand?.toString()} />
|
||||
</span>
|
||||
{#if element.$id === backupMethod}
|
||||
<Pill>Backup</Pill>
|
||||
{:else if element.$id === defaultMethod}
|
||||
<Pill>Default</Pill>
|
||||
{/if}
|
||||
</span>
|
||||
{#if !!defaultMethod && element.$id !== defaultMethod && group === element.$id && showSetAsDefault}
|
||||
<ul>
|
||||
<InputChoice
|
||||
bind:value={setAsDefault}
|
||||
id="default"
|
||||
label="Set as default payment method for this organization" />
|
||||
</ul>
|
||||
{/if}
|
||||
</span>
|
||||
</slot>
|
||||
</svelte:fragment>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
{#if total}
|
||||
{#each elements as element}
|
||||
{@const value = element[variableName]?.toString()}
|
||||
<div class="box" data-private>
|
||||
<ul class="box" data-private>
|
||||
<InputRadio
|
||||
id={`${name}-${value}`}
|
||||
{value}
|
||||
@@ -22,11 +22,11 @@
|
||||
disabled={disabledCondition ? value === disabledCondition : false}>
|
||||
<slot name="element" {element} />
|
||||
</InputRadio>
|
||||
</div>
|
||||
</ul>
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
<div class="box">
|
||||
<ul class="box">
|
||||
{#if total}
|
||||
<InputRadio id="payment-method" value={null} {name} bind:group>
|
||||
<slot name="new">
|
||||
@@ -37,5 +37,5 @@
|
||||
{#if group === null}
|
||||
<slot />
|
||||
{/if}
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
event="download_invoice">
|
||||
Download PDF
|
||||
</DropListLink>
|
||||
{#if status === 'overdue' || status === 'failed'}
|
||||
{#if status === 'overdue' || status === 'failed' || true}
|
||||
<DropListItem
|
||||
icon="refresh"
|
||||
on:click={() => {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { CreditCardBrandImage, FakeModal } from '$lib/components';
|
||||
import { Button, FormList, InputRadio, InputText } from '$lib/elements/forms';
|
||||
import { FakeModal } from '$lib/components';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { initializeStripe, isStripeInitialized, submitStripeCard } from '$lib/stores/stripe';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { submitStripeCard } from '$lib/stores/stripe';
|
||||
import { onMount } from 'svelte';
|
||||
import type { PaymentList } from '$lib/sdk/billing';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { Pill } from '$lib/elements';
|
||||
import { PaymentBoxes } from '$lib/components/billing';
|
||||
|
||||
export let show = false;
|
||||
export let isBackup = false;
|
||||
@@ -18,10 +18,6 @@
|
||||
let selectedPaymentMethodId: string;
|
||||
let name: string;
|
||||
let error: string;
|
||||
let element: HTMLDivElement;
|
||||
let loader: HTMLDivElement;
|
||||
|
||||
let observer: MutationObserver;
|
||||
|
||||
onMount(async () => {
|
||||
methods = await sdk.forConsole.billing.listPaymentMethods();
|
||||
@@ -32,27 +28,6 @@
|
||||
selectedPaymentMethodId = isBackup
|
||||
? $organization.backupPaymentMethodId
|
||||
: $organization.paymentMethodId;
|
||||
|
||||
observer = new MutationObserver((mutationsList) => {
|
||||
for (let mutation of mutationsList) {
|
||||
if (mutation.type === 'childList') {
|
||||
if (mutation.addedNodes.length > 0) {
|
||||
for (let node of Array.from(mutation.addedNodes)) {
|
||||
if (
|
||||
node instanceof Element &&
|
||||
node.className.toLowerCase().includes('__privatestripeelement')
|
||||
) {
|
||||
loader.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
observer.disconnect();
|
||||
});
|
||||
|
||||
async function handleSubmit() {
|
||||
@@ -105,19 +80,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: if (selectedPaymentMethodId === null && !$isStripeInitialized && show) {
|
||||
initializeStripe();
|
||||
}
|
||||
|
||||
$: filteredMethods = methods?.paymentMethods.filter((method) => !!method?.last4);
|
||||
|
||||
$: if (show) {
|
||||
isStripeInitialized.set(false);
|
||||
if (element) {
|
||||
observer.observe(element, { childList: true });
|
||||
}
|
||||
}
|
||||
|
||||
$: if (!show) {
|
||||
selectedPaymentMethodId = null;
|
||||
}
|
||||
@@ -131,70 +95,16 @@
|
||||
headerDivider={false}
|
||||
title="Replace payment method">
|
||||
<p class="text">Replace the existing payment method for your organization.</p>
|
||||
<FormList>
|
||||
<div class:boxes-wrapper={methods?.total}>
|
||||
{#if methods?.total}
|
||||
{#each filteredMethods as method}
|
||||
<div class="box">
|
||||
<InputRadio
|
||||
id={`payment-method-${method.$id}`}
|
||||
disabled={isBackup
|
||||
? method.$id === $organization.paymentMethodId
|
||||
: method.$id === $organization.backupPaymentMethodId}
|
||||
value={method.$id}
|
||||
name="payment"
|
||||
bind:group={selectedPaymentMethodId}>
|
||||
<span class="u-flex u-gap-16 u-main-space-between">
|
||||
<span
|
||||
class="u-flex u-cross-center u-gap-8"
|
||||
style="padding-inline:0.25rem">
|
||||
<span>
|
||||
<span class="u-capitalize">{method.brand}</span> ending in {method.last4}</span>
|
||||
<CreditCardBrandImage brand={method.brand} />
|
||||
</span>
|
||||
{#if method.$id === $organization.backupPaymentMethodId}
|
||||
<Pill>Backup</Pill>
|
||||
{:else if method.$id === $organization.paymentMethodId}
|
||||
<Pill>Default</Pill>
|
||||
{/if}
|
||||
</span>
|
||||
</InputRadio>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
<div class="box">
|
||||
{#if methods?.total}
|
||||
<InputRadio
|
||||
id="payment-method"
|
||||
value={null}
|
||||
name="payment"
|
||||
bind:group={selectedPaymentMethodId}>
|
||||
<span style="padding-inline:0.25rem">Add new payment method</span>
|
||||
</InputRadio>
|
||||
{/if}
|
||||
{#if selectedPaymentMethodId === null}
|
||||
<FormList>
|
||||
<InputText
|
||||
id="name"
|
||||
label="Cardholder name"
|
||||
placeholder="Cardholder name"
|
||||
bind:value={name}
|
||||
required
|
||||
autofocus={true} />
|
||||
<div class="aw-stripe-container" data-private>
|
||||
<div class="loader-container" bind:this={loader}>
|
||||
<div class="loader"></div>
|
||||
</div>
|
||||
<div id="payment-element" bind:this={element}>
|
||||
<!-- Stripe will create form elements here -->
|
||||
</div>
|
||||
</div>
|
||||
</FormList>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</FormList>
|
||||
<PaymentBoxes
|
||||
methods={filteredMethods}
|
||||
bind:name
|
||||
bind:group={selectedPaymentMethodId}
|
||||
defaultMethod={$organization?.paymentMethodId}
|
||||
backupMethod={$organization?.backupPaymentMethodId}
|
||||
disabledCondition={isBackup
|
||||
? $organization.paymentMethodId
|
||||
: $organization.backupPaymentMethodId} />
|
||||
<svelte:fragment slot="footer">
|
||||
<Button text on:click={() => (show = false)}>Cancel</Button>
|
||||
<Button
|
||||
@@ -206,17 +116,3 @@
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
</FakeModal>
|
||||
|
||||
<style lang="scss">
|
||||
.aw-stripe-container {
|
||||
min-height: 295px;
|
||||
position: relative;
|
||||
.loader-container {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -11,12 +11,22 @@
|
||||
import { confirmPayment } from '$lib/stores/stripe';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { toLocaleDate } from '$lib/helpers/date';
|
||||
import { PaymentBoxes } from '$lib/components/billing';
|
||||
import { paymentMethods } from '$lib/stores/billing';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
const endpoint = VARS.APPWRITE_ENDPOINT ?? `${$page.url.origin}/v1`;
|
||||
export let show = false;
|
||||
export let invoice: Invoice;
|
||||
let error: string = null;
|
||||
let isButtonDisabled = false;
|
||||
let name: string;
|
||||
let paymentMethodId: string;
|
||||
let setAsDefault = false;
|
||||
|
||||
onMount(() => {
|
||||
paymentMethodId = $organization.paymentMethodId;
|
||||
});
|
||||
|
||||
async function handleSubmit() {
|
||||
isButtonDisabled = true;
|
||||
@@ -42,6 +52,7 @@
|
||||
isButtonDisabled = false;
|
||||
}
|
||||
}
|
||||
$: filteredMethods = $paymentMethods?.paymentMethods.filter((method) => !!method?.last4);
|
||||
|
||||
$: if (!show) {
|
||||
invoice = null;
|
||||
@@ -69,6 +80,15 @@
|
||||
href={`${endpoint}/organizations/${$page.params.organization}/invoices/${invoice.$id}/view`}>
|
||||
View invoice
|
||||
</Button>
|
||||
|
||||
<PaymentBoxes
|
||||
methods={filteredMethods}
|
||||
defaultMethod={$organization?.paymentMethodId}
|
||||
backupMethod={$organization?.backupPaymentMethodId}
|
||||
showSetAsDefault
|
||||
bind:setAsDefault
|
||||
bind:name
|
||||
bind:group={paymentMethodId} />
|
||||
<svelte:fragment slot="footer">
|
||||
<Button text on:click={() => (show = false)}>Cancel</Button>
|
||||
<Button secondary submit disabled={isButtonDisabled}>Retry</Button>
|
||||
|
||||
Reference in New Issue
Block a user