Skip to content

Commit

Permalink
feat: reorganize billing section
Browse files Browse the repository at this point in the history
  • Loading branch information
kizivat committed Jul 18, 2024
1 parent 8425355 commit a644e32
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 53 deletions.
1 change: 1 addition & 0 deletions src/routes/(app)/settings/billing/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const load: PageServerLoad = async () => {
products: toSortedProducts(
products.map((product) => ({
...product,
default_price: product.default_price as Stripe.Price, // just force the type
prices: prices.filter((price) => price.product === product.id),
})),
),
Expand Down
112 changes: 60 additions & 52 deletions src/routes/(app)/settings/billing/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,75 +1,83 @@
<script lang="ts">
import Button from '$lib/components/ui/button/button.svelte';
import * as Card from '$lib/components/ui/card';
import Input from '$lib/components/ui/input/input.svelte';
import PriceAmount from './components/PriceAmount.svelte';
import PriceDescription from './components/PriceDescription.svelte';
export let data;
let { products } = data;
$: withDefaultPrices = products.map((product) => {
return {
...product,
prices: product.prices.filter(
(price) => price.id === product.default_price.id,
),
};
});
$: withOtherPrices = products.map((product) => {
return {
...product,
prices: product.prices.filter(
(price) => price.id !== product.default_price.id,
),
};
});
$: console.log(data, withDefaultPrices, withOtherPrices);
</script>

<svelte:head>
<title>Billing | Settings</title>
</svelte:head>

<h2 class="text-xl font-semibold">Billing</h2>
<section>
<ol class="grid grid-cols-3 gap-4">
{#each data.products as product}
{#each product.prices.filter((price) => price.product === product.id) as price}

<section class="flex flex-col gap-3">
<h3 class="text-lg font-semibold">Default Prices</h3>
<ol class="grid gap-4 lg:grid-cols-3">
{#each withDefaultPrices as product}
{#each product.prices as price}
<li
class="[&:nth-child(2)>.bg-card]:border-2 [&:nth-child(2)>.bg-card]:border-primary [&:nth-child(2)]:scale-105"
>
<Card.Root>
<Card.Header>
<Card.Title tag="h3">{product.name}</Card.Title>
<Card.Description>
<PriceDescription {price} />
</Card.Description>
</Card.Header>
<Card.Content>
<PriceAmount {price} />
</Card.Content>
<Card.Footer>
<Button href="/checkout/{price.id}">Select Plan</Button>
</Card.Footer>
</Card.Root>
</li>
{/each}
{/each}
</ol>
</section>

<section class="flex flex-col gap-3">
<h3 class="text-lg font-semibold">Other Prices</h3>
<ol class="grid gap-4 lg:grid-cols-3">
{#each withOtherPrices as product}
{#each product.prices as price}
<li>
<Card.Root>
<Card.Header>
<Card.Title tag="h3">{product.name}</Card.Title>
<Card.Description>
{#if price.recurring?.interval}
{price.recurring.interval === 'month'
? 'Monthly Plan'
: 'Annual Plan'}
{:else if price.custom_unit_amount}
Pay what you want
{:else}
One-time payment
{/if}
<PriceDescription {price} />
</Card.Description>
</Card.Header>
<Card.Content>
{#if price.custom_unit_amount !== null}
<div class="flex flex-row flex-nowrap items-center gap-2">
<span class="text-4xl">
{Intl.NumberFormat('en-US', {
style: 'currency',
maximumFractionDigits: 0,
minimumFractionDigits: 0,
currency: price.currency,
})
.format(0)
.replace(/\d/g, '')}
</span>
<Input
type="number"
class="text-xl"
value={price.custom_unit_amount.preset
? price.custom_unit_amount.preset / 100
: ''}
placeholder="Enter amount"
/>
</div>
{:else if price.unit_amount !== null}
<div>
<span class="text-4xl">
{Intl.NumberFormat('en-US', {
style: 'currency',
currency: price.currency,
maximumFractionDigits: 0,
currencySign: 'accounting',
}).format(price.unit_amount / 100)}
</span>
{#if price.recurring}
<span class="text-muted-foreground">
/ {price.recurring.interval}
</span>
{/if}
</div>
{/if}
<PriceAmount {price} />
</Card.Content>
<Card.Footer>
<Button href="/checkout/{price.id}">Select Plan</Button>
Expand Down
46 changes: 46 additions & 0 deletions src/routes/(app)/settings/billing/components/PriceAmount.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<script lang="ts">
import { Input } from '$lib/components/ui/input';
import Stripe from 'stripe';
export let price: Stripe.Price;
</script>

{#if price.custom_unit_amount !== null}
<div class="flex flex-row flex-nowrap items-center gap-2">
<span class="text-4xl">
<!-- Just to get the currency symbol programatically -->
{Intl.NumberFormat('en-US', {
style: 'currency',
maximumFractionDigits: 0,
minimumFractionDigits: 0,
currency: price.currency,
})
.format(0)
.replace(/\d/g, '')}
</span>
<Input
type="number"
class="text-xl"
value={price.custom_unit_amount.preset
? price.custom_unit_amount.preset / 100
: ''}
placeholder="Enter amount"
/>
</div>
{:else if price.unit_amount !== null}
<div>
<span class="text-4xl">
{Intl.NumberFormat('en-US', {
style: 'currency',
currency: price.currency,
maximumFractionDigits: 0,
currencySign: 'accounting',
}).format(price.unit_amount / 100)}
</span>
{#if price.recurring}
<span class="text-muted-foreground">
/ {price.recurring.interval}
</span>
{/if}
</div>
{/if}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script lang="ts">
import Stripe from 'stripe';
export let price: Stripe.Price;
</script>

{#if price.recurring?.interval}
{price.recurring.interval === 'month' ? 'Monthly Plan' : 'Annual Plan'}
{:else if price.custom_unit_amount}
Pay what you want
{:else}
One-time payment
{/if}
5 changes: 4 additions & 1 deletion src/routes/(app)/settings/billing/stripe-helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import type Stripe from 'stripe';

export type ProductWithPrices = Stripe.Product & { prices: Stripe.Price[] };
export type ProductWithPrices = Stripe.Product & {
default_price: Stripe.Price;
prices: Stripe.Price[];
};

export function toSortedPrices(prices: Stripe.Price[]): Stripe.Price[] {
const result = [...prices];
Expand Down

0 comments on commit a644e32

Please sign in to comment.