Skip to content

Fix various issues in billing flows #1945

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
May 30, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"@appwrite.io/pink-icons": "0.25.0",
"@appwrite.io/pink-icons-svelte": "^2.0.0-RC.1",
"@appwrite.io/pink-legacy": "^1.0.3",
"@appwrite.io/pink-svelte": "https://try-module.cloud/-/@appwrite/@appwrite.io/pink-svelte@4b056c8",
"@appwrite.io/pink-svelte": "https://try-module.cloud/-/@appwrite/@appwrite.io/pink-svelte@d74b893",
"@popperjs/core": "^2.11.8",
"@sentry/sveltekit": "^8.38.0",
"@stripe/stripe-js": "^3.5.0",
Expand Down Expand Up @@ -65,6 +65,7 @@
"@typescript-eslint/eslint-plugin": "^8.28.0",
"@typescript-eslint/parser": "^8.28.0",
"@vitest/ui": "^3.0.9",
"color": "^5.0.0",
"eslint": "^9.23.0",
"eslint-config-prettier": "^10.1.0",
"eslint-plugin-svelte": "^3.3.3",
Expand Down
44 changes: 39 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 14 additions & 4 deletions src/lib/components/billing/paymentModal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@

let name: string;
let error: string;
let modal: FakeModal;

async function handleSubmit() {
try {
const card = await submitStripeCard(name, page?.params?.organization ?? null);
show = false;
modal.closeModal();
invalidate(Dependencies.PAYMENT_METHODS);
dispatch('submit', card);
addNotification({
Expand Down Expand Up @@ -65,7 +66,12 @@
}
</script>

<FakeModal bind:show title="Add payment method" bind:error onSubmit={handleSubmit}>
<FakeModal
bind:this={modal}
bind:show
title="Add payment method"
bind:error
onSubmit={handleSubmit}>
<slot />
<InputText
id="name"
Expand All @@ -82,7 +88,10 @@
</div>
{/if}

<div class="stripe-element" bind:this={element}>
<div
style:display={isLoading ? 'none' : 'initial'}
class="stripe-element"
bind:this={element}>
<!-- Stripe will create form elements here -->
</div>
</div>
Expand All @@ -105,7 +114,8 @@
.loader-element {
width: 100%;
align-self: center;
justify-items: end;
justify-content: center;
display: flex;
}
}
</style>
5 changes: 3 additions & 2 deletions src/lib/components/creditCardInfo.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
</Layout.Stack>
</Table.Cell>
<Table.Cell column="name" {root}>{paymentMethod?.name}</Table.Cell>
<Table.Cell column="expiry" {root}
>{paymentMethod?.expiryMonth}/{paymentMethod?.expiryYear}</Table.Cell>
<Table.Cell column="expiry" {root}>
{paymentMethod?.expiryMonth}/{paymentMethod?.expiryYear}
</Table.Cell>
<Table.Cell column="status" {root}>
{#if paymentMethod?.lastError || paymentMethod?.expired}
<Popover let:toggle>
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/fakeModal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
}
}

function closeModal() {
export function closeModal() {
document.documentElement.classList.remove('u-overflow-hidden');
show = false;
}
Expand Down
2 changes: 0 additions & 2 deletions src/lib/components/filters/filtersBottomSheet.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,6 @@
items: filterCols.map((col) => {
return {
name: col.title,
onClick: () =>
console.log(subSheets.find((sheet) => sheet?.title === col?.title)),
subMenu: subSheets.find((sheet) => sheet?.title === col?.title),
trailingIcon: IconChevronRight
};
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/mfaChallengeFormList.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
{#if enabledFactors.length > 1}
<span class="with-separators eyebrow-heading-3">or</span>
<div class="u-flex-vertical u-gap-8">
{#if factors.totp && challengeType != null && challengeType != AuthenticationFactor.Totp}
{#if factors.totp && challengeType !== null && challengeType !== AuthenticationFactor.Totp}
<Button
secondary
fullWidth
Expand Down
4 changes: 4 additions & 0 deletions src/lib/components/navbar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import { user } from '$lib/stores/user';
import { Click, trackEvent } from '$lib/actions/analytics';
import type { HTMLAttributes } from 'svelte/elements';
import { beforeNavigate } from '$app/navigation';

let showSupport = false;

Expand Down Expand Up @@ -115,6 +116,9 @@

$: currentOrg = organizations.find((org) => org.isSelected);
$: selectedProject = currentOrg?.projects.find((project) => project.isSelected);
beforeNavigate(() => {
showAccountMenu = false;
});
</script>

<Navbar.Base {...$$props}>
Expand Down
67 changes: 46 additions & 21 deletions src/lib/stores/stripe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { addNotification } from './notifications';
import { organization } from './organization';
import { base } from '$app/paths';
import { ThemeLightCloud } from '$themes';
import { ThemeDarkCloud, ThemeLightCloud } from '$themes';
import Color from 'color';

export const stripe = writable<Stripe>();
let paymentMethod: PaymentMethodData;
Expand Down Expand Up @@ -185,37 +186,46 @@ export async function confirmSetup(
}
}

function toRGB(color: string): string {
return Color(color).rgb().string();
}

const appearanceLight: Appearance = {
variables: {
fontSizeBase: ThemeLightCloud['font-size-s'],
fontSizeSm: ThemeLightCloud['font-size-s'],
colorPrimary: ThemeLightCloud['neutral-700'],
colorText: ThemeLightCloud['neutral-700'],
colorBackground: 'rgb(250, 250, 251)',
colorDanger: ThemeLightCloud['fgcolor-error'],
fontFamily: ThemeLightCloud['font-family-sansserif'],
colorPrimary: toRGB(ThemeLightCloud['neutral-700']),
colorTextSecondary: toRGB(ThemeLightCloud['neutral-700']),
colorText: toRGB(ThemeLightCloud['neutral-700']),
colorBackground: toRGB(ThemeLightCloud['neutral-25']),
colorDanger: toRGB(ThemeLightCloud['web-red-700']),
fontFamily:
ThemeLightCloud['font-family-sansserif'] +
', system-ui, -apple-system, BlinkMacSystemFont, sans-serif',
borderRadius: ThemeLightCloud['base-8']
},
rules: {
'.Label': {
color: ThemeLightCloud['neutral-700'],
color: toRGB(ThemeLightCloud['neutral-700']),
marginBottom: ThemeLightCloud['base-8'],
fontWeight: '500'
},
'.Input': {
padding: ThemeLightCloud['base-6'],
paddingLeft: ThemeLightCloud['base-12']
paddingLeft: ThemeLightCloud['base-12'],
outlineOffset: '-1px'
},
'.Input:hover': {
border: 'solid 1px rgb(195, 195, 198)',
border: '1px solid ' + toRGB(ThemeLightCloud['neutral-250']),
boxShadow: 'none'
},
'.Input:focus': {
border: 'solid 1px rgb(195, 195, 198)',
border: '1px solid ' + toRGB(ThemeLightCloud['neutral-250']),
outline: '2px solid ' + toRGB(ThemeLightCloud['neutral-250']),
boxShadow: 'none'
},
'.Input::placeholder': {
color: '#C4C6D7'
color: toRGB(ThemeLightCloud['neutral-250'])
},
'.Input--invalid': {
border: 'solid 1px var(--colorDanger)',
Expand All @@ -226,25 +236,40 @@ const appearanceLight: Appearance = {

const appearanceDark = {
variables: {
colorPrimary: '#606a7b',
colorText: 'rgb(195, 195, 198)',
colorBackground: 'rgb(24, 24, 27)',
colorDanger: '#FF453A',
fontFamily: 'Inter, arial, sans-serif',
borderRadius: '4px',
spacingGridRow: '16px'
fontSizeBase: ThemeDarkCloud['font-size-s'],
fontSizeSm: ThemeDarkCloud['font-size-s'],
colorPrimary: toRGB(ThemeDarkCloud['neutral-250']),
colorText: toRGB(ThemeDarkCloud['neutral-250']),
colorTextSecondary: toRGB(ThemeDarkCloud['neutral-250']),
colorBackground: toRGB(ThemeDarkCloud['neutral-900']),
colorDanger: toRGB(ThemeDarkCloud['web-red-500']),
fontFamily:
ThemeLightCloud['font-family-sansserif'] +
', system-ui, -apple-system, BlinkMacSystemFont, sans-serif',
borderRadius: ThemeDarkCloud['base-8']
},
rules: {
'.Label': {
color: toRGB(ThemeDarkCloud['neutral-250']),
marginBottom: ThemeDarkCloud['base-8'],
fontWeight: '500'
},
'.Input': {
padding: ThemeDarkCloud['base-6'],
paddingLeft: ThemeDarkCloud['base-12'],
outlineOffset: '-1px'
},
'.Input:hover': {
border: 'solid 1px rgb(87, 87, 92)',
border: '1px solid ' + toRGB(ThemeDarkCloud['neutral-500']),
boxShadow: 'none'
},
'.Input:focus': {
border: 'solid 1px rgb(87, 87, 92)',
border: '1px solid ' + toRGB(ThemeDarkCloud['neutral-500']),
outline: '2px solid ' + toRGB(ThemeDarkCloud['neutral-500']),
boxShadow: 'none'
},
'.Input::placeholder': {
color: 'rgb(87, 87, 92)'
color: toRGB(ThemeDarkCloud['neutral-500'])
},
'.Input--invalid': {
border: 'solid 1px var(--colorDanger)',
Expand Down
2 changes: 1 addition & 1 deletion src/routes/(console)/account/header.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
...permanentTabs,
{
href: `${path}/payments`,
title: 'Payment details',
title: 'Payments',
event: 'payments',
hasChildren: true
}
Expand Down
24 changes: 17 additions & 7 deletions src/routes/(console)/account/payments/paymentMethods.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@
$: filteredMethods = $paymentMethods?.paymentMethods.filter(
(method: PaymentMethodData) => !!method?.last4
);

$: hasLinkedOrgs = filteredMethods.some((method) =>
orgList.some(
(org) => method.$id === org.paymentMethodId || method.$id === org.backupPaymentMethodId
)
);
$: hasPaymentError = filteredMethods.some((method) => method?.lastError || method?.expired);
</script>

<CardGrid>
Expand All @@ -50,18 +57,21 @@
<Table.Root
let:root
columns={[
{ id: 'cc' },
{ id: 'name' },
{ id: 'expiry' },
{ id: 'status' },
{ id: 'links' },
{ id: 'cc', width: 140 },
{ id: 'name', width: { min: 140 } },
{ id: 'expiry', width: 100 },
{ id: 'status', width: 110, hide: !hasPaymentError },
{ id: 'links', width: 190, hide: !hasLinkedOrgs },
{ id: 'actions', width: 40 }
]}>
<svelte:fragment slot="header" let:root>
<Table.Header.Cell column="cc" {root}>Credit card</Table.Header.Cell>
<Table.Header.Cell column="name" {root}>Name</Table.Header.Cell>
<Table.Header.Cell column="expiration" {root}
>Expiration date</Table.Header.Cell>
<Table.Header.Cell column="expiration" {root}>
Expiration date
</Table.Header.Cell>
<Table.Header.Cell column="status" {root} />
<Table.Header.Cell column="links" {root} />
<Table.Header.Cell column="actions" {root} />
</svelte:fragment>
{#each filteredMethods as paymentMethod, i}
Expand Down
4 changes: 2 additions & 2 deletions src/routes/(console)/account/updateMfa.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,11 @@
<span class="icon-device-mobile" aria-hidden="true"></span>
</div>
<Layout.Stack gap="xxxs">
<Layout.Stack gap="s">
<Layout.Stack gap="s" direction="row">
<Typography.Text variant="m-600"
>Authenticator app</Typography.Text>
{#if $factors.totp}
<Pill>connected</Pill>
<Badge variant="secondary" content="connected" size="s" />
{/if}
</Layout.Stack>
<Typography.Text variant="m-400">
Expand Down
Loading
Loading