enh: pricing page refactor

This commit is contained in:
Ben Allfree 2025-01-11 03:26:15 -08:00
parent f60ac04703
commit a3be3b7deb
19 changed files with 241 additions and 360 deletions

View File

@ -1,8 +1,9 @@
<script lang="ts">
import { PLAN_NAMES, SubscriptionType } from 'pockethost/common'
import { stats, userStore } from '$util/stores'
import { stats, userStore, isUserLoggedIn } from '$util/stores'
import PricingCard from '$components/PricingCard.svelte'
const TOTAL_QTY = 150
export let priceMonthly: [number, string?, number?] = [
359,
'once, use forever',
@ -18,21 +19,22 @@
<PricingCard
name={`${PLAN_NAMES[SubscriptionType.Flounder]}`}
qtyRemaining={1000 - $stats.total_flounder_subscribers}
qtyMax={1000}
qtyRemaining={TOTAL_QTY - $stats.total_flounder_subscribers}
qtyMax={TOTAL_QTY}
description="Epic elite! The Flounder's Edition is almost as good as the Founder's edition, and you'll help PocketHost go global."
{priceMonthly}
requireAuthenticatedUser
checkoutMonthURL="https://store.pockethost.io/buy/d4b2d062-429c-49b4-9cdc-853aaeb17e20?checkout[custom][user_id]=${$userStore?.id}&checkout[email]=${$userStore?.email}"
checkoutMonthURL={$isUserLoggedIn
? 'https://store.pockethost.io/buy/d4b2d062-429c-49b4-9cdc-853aaeb17e20?checkout[custom][user_id]=${$userStore?.id}&checkout[email]=${$userStore?.email}'
: `/get-started`}
features={[
`Unlimited instances`,
'Unlimited instances',
'Unlimited bandwidth',
'Unlimited storage & files',
`Private #onlyflounders Discord channel`,
`Priority support`,
`Commemorative Flounder's badge`,
`PocketHost t-shirt`,
`#onlyflounders private discord channel`,
`-Girlfriend`,
]}
fundingGoals={[
`Global regions (approx 40)`,
`Global low latency from anywhere`,
]}
/>

View File

@ -80,7 +80,7 @@
</script>
<!-- It's a card, so use card -->
<div class="card bg-base-100 w-96 shadow-xl">
<div class="card w-96 shadow-xl bg-neutral text-neutral-content">
<div class="card-body items-center text-center">
<div class="card-title">
<h2 id="tier-startup" class="text-2xl font-semibold leading-8 text-white">

View File

@ -7,12 +7,12 @@
name: 'Riddge Mussington',
title: '',
quote:
"I use pockethost for a couple projects and I think you would be hard pressed to find a better hosted provider for pocketbase projects. It is very easy to use and setup and secure by default because it uses pocketbase under the hood, it's also very fast at shipping pocketbase updates and maintaining uptime and fixing issues, it's truly a god chosen database especially with the very generous free tier that I wish will never change, I'm currently building my next side hustle with it so I can't wait for it to grow larger",
"I use pockethost for a couple projects and I think you would be hard pressed to find a better hosted provider for pocketbase projects. It is very easy to use and setup and secure by default because it uses pocketbase under the hood, it's also very fast at shipping pocketbase updates and maintaining uptime and fixing issues, it's truly a god chosen database, I'm currently building my next side hustle with it so I can't wait for it to grow larger",
},
{
name: 'H.Mohamed',
title: '',
quote: 'Great BaaS option for simpler apps with a generous free plan.',
quote: 'Great BaaS option.',
},
{
name: 'Damian Kennedy',
@ -48,27 +48,32 @@
]
</script>
<div class="flex flex-wrap justify-center gap-4">
{#each testimonials as testimonial}
<div class="card bg-neutral shadow-xl w-96">
<div class="card-body">
<div class="flex mb-2">
<Fa icon={faStar} class="text-warning" />
<Fa icon={faStar} class="text-warning" />
<Fa icon={faStar} class="text-warning" />
<Fa icon={faStar} class="text-warning" />
<Fa icon={faStar} class="text-warning" />
</div>
<p class="mb-4">{testimonial.quote}</p>
<div class="card-actions">
<div>
<p class="font-bold">{testimonial.name}</p>
{#if testimonial.title}
<p class="text-sm opacity-75">{testimonial.title}</p>
{/if}
<div class="flex-1 p-5 flex flex-col">
<div class="text-3xl text-center pb-5">Testimonials</div>
<div class="flex flex-wrap gap-6 justify-center">
<div class="flex flex-wrap justify-center gap-4">
{#each testimonials as testimonial}
<div class="card bg-neutral shadow-xl w-96">
<div class="card-body">
<div class="flex mb-2">
<Fa icon={faStar} class="text-warning" />
<Fa icon={faStar} class="text-warning" />
<Fa icon={faStar} class="text-warning" />
<Fa icon={faStar} class="text-warning" />
<Fa icon={faStar} class="text-warning" />
</div>
<p class="mb-4">{testimonial.quote}</p>
<div class="card-actions">
<div>
<p class="font-bold">{testimonial.name}</p>
{#if testimonial.title}
<p class="text-sm opacity-75">{testimonial.title}</p>
{/if}
</div>
</div>
</div>
</div>
</div>
{/each}
</div>
{/each}
</div>
</div>

View File

@ -0,0 +1,5 @@
<script lang="ts">
import Paywall from '../../(static)/pricing/Paywall.svelte'
</script>
<Paywall />

View File

@ -1,5 +0,0 @@
<script lang="ts">
import Paywall from './Paywall.svelte'
</script>
<Paywall />

View File

@ -1,14 +0,0 @@
<script lang="ts">
export let feature: any
</script>
<div class="card bg-accent text-accent-content shadow-xl mr-auto ml-auto">
<div class="card-body">
<h2 class="card-title">{feature.title}</h2>
{#if feature.img}
<enhanced:img src={feature.img} />
{/if}
<p>{@html feature.description}</p>
<div class="card-actions justify-end"></div>
</div>
</div>

View File

@ -1,103 +0,0 @@
<script lang="ts">
import Card from './Card.svelte'
import regions from './regions.png?enhanced'
import Testimonials from '$src/components/Testimonials.svelte'
import { userStore } from '$src/util/stores'
let idx = 0
const features = [
{
title: 'PocketBase at Global Scale',
img: regions,
description:
'PocketHost has edges in 40+ regions. Your users connect through our global network of edge servers, then our internal VPN handles world-class routing to your data, wherever it resides.',
button: 'Unlock Access Now',
},
{
title: 'Developer-Friendly Pricing',
description: `Join the pirate ship for just $5 per instance. After 5 instances? It's on us, because we like how you think.`,
button: 'Unlock Access Now',
},
{
title: 'Risk-free Trial',
description: `Test-drive PocketHost for 7 days. Credit card required.`,
button: 'Unlock Access Now',
},
{
title: 'Pay Once, Use Forever',
description: `Join the Flounder's crew LTD. One payment, lifetime access to the Pro tier (up to 250 instances). Once these spots are gone, they're gone forever.`,
button: 'Unlock Access Now',
},
{
title: 'No Usage Police',
description: `Forget usage meters and quotas. Our Fair Use policy means you can build without limits. Gone viral? We've got your back. No questions asked.`,
button: 'Unlock Access Now',
},
{
title: 'Ludicrous Speed',
description: `Our global network moves at the speed of thought. 10-30ms average time-to-edge means your apps feel like magic. Your users won't know what hit them.`,
button: 'Unlock Access Now',
},
{
title: 'Early Access to New Tech',
description: `Get your hands on our experimental <a href="/blog/announcing-pocker" class="link">Pocker</a> tech before anyone else. Help us push the boundaries of what's possible.`,
button: 'Unlock Access Now',
},
{
title: 'Unshakeable',
description:
'With 99.95% uptime, your apps stay up while others go down. Sleep easy knowing your creation is always ready to serve.',
button: 'Unlock Access Now',
},
{
title: 'Death to DevOps',
description: `Focus on the cool stuff. Let us handle the boring bits - scaling, backups, maintenance. It's time to build something legendary.`,
button: 'Unlock Access Now',
},
{
title: 'Hack More, Pay Less',
description:
'Why waste time managing servers? Put your resources into building the next big thing. Your wallet (and your future app) will thank you.',
button: 'Unlock Access Now',
},
{
title: 'Power to the Builders',
description:
'Join us in building the future. Every subscription supports the creators and contributors of the 100% open-source PocketBase and PocketHost projects.',
button: 'Unlock Access Now',
},
]
$: feature = features[idx]!
</script>
<div class="prose ml-auto mr-auto">
<p class="text-3xl text-center">PocketHost Access</p>
<p class="text-2xl text-center">
<span class="text-primary">$5/instance</span> or
<span class="text-primary">$25/unlimited</span>
</p>
<p class="text-xl text-accent text-center">7 Day Free Trial</p>
<div class="flex justify-center mt-10 mb-10">
<a
class="btn btn-warning btn-lg"
style="z-index: 1000"
href={`https://store.pockethost.io/buy/d4b2d062-429c-49b4-9cdc-853aaeb17e20?checkout[custom][user_id]=${$userStore?.id}&checkout[email]=${$userStore?.email}`}
>{feature.button}</a
>
</div>
{#each features as feature}
<div class="not-prose mb-10">
<Card {feature} />
</div>
{/each}
<Testimonials />
</div>
<a
class="btn btn-warning btn-block btn-lg rounded-none fixed bottom-0"
style="z-index: 1000"
href={`https://store.pockethost.io/buy/d4b2d062-429c-49b4-9cdc-853aaeb17e20?checkout[custom][user_id]=${$userStore?.id}&checkout[email]=${$userStore?.email}`}
>{feature.button}</a
>

View File

@ -24,10 +24,10 @@ You can get started easily on PocketHost by purchasing an instance.
**Unlimited Doesn't Mean Infinite**
While we offer ample limits for projects, storage, bandwidth, and CPU, all usage is subject to our [Fair Use Policy](/terms):
While we offer ample limits for projects, storage, bandwidth, and CPU, all usage is subject to Fair Use:
- **Fair Use Basis**: Use resources similarly to the average active app on our platform.
- **Resource Management**: Your app scales up or down based on its needs.
- **Good Citizenship**: Be mindful of resource consumption to ensure a positive experience for all users.
For more details, please refer to our Fair Use Policy in the [Terms of Service](/terms).
For more details, please refer to our complete [Fair Use Policy](/terms).

View File

@ -1,29 +1,5 @@
<script lang="ts">
import PricingTable from './PricingTable.svelte'
import FounderCard from '$components/FounderCard.svelte'
import FlounderCard from '$components/FlounderCard.svelte'
import Testimonials from '$components/Testimonials.svelte'
import Paywall from './Paywall.svelte'
</script>
<!-- Flex them up -->
<div class="flex flex-col mt-2">
<div class="flex-1 text-center font-bold text-primary text-4xl mb-6">
Pricing
</div>
<div class="flex-1 bg-neutral text-neutral-content p-5 flex flex-col">
<div class="text-xl text-center pb-5">Rare deals</div>
<div class="flex flex-wrap gap-6 justify-center">
<div class="w-96">
<FounderCard />
</div>
<div class="w-96">
<FlounderCard endDate={new Date(2024, 11, 3)} />
</div>
</div>
</div>
<PricingTable />
<Testimonials />
</div>
<Paywall />

View File

@ -0,0 +1,26 @@
<script lang="ts">
import { userStore } from '$util/stores'
import UserLoggedIn from '$components/guards/UserLoggedIn.svelte'
import UserLoggedOut from '$components/guards/UserLoggedOut.svelte'
export let fixed = false
</script>
<UserLoggedIn>
<a
class="btn btn-warning btn-lg {fixed
? ' btn-block rounded-none fixed bottom-0'
: ''}"
style="z-index: 1000"
href={`https://store.pockethost.io/buy/d4b2d062-429c-49b4-9cdc-853aaeb17e20?checkout[custom][user_id]=${$userStore?.id}&checkout[email]=${$userStore?.email}`}
>Unlock Access Now</a
>
</UserLoggedIn>
<UserLoggedOut>
<a
class="btn btn-warning btn-lg {fixed
? ' btn-block rounded-none fixed bottom-0'
: ''}"
href="/get-started">Get Started</a
>
</UserLoggedOut>

View File

@ -0,0 +1,23 @@
<script lang="ts">
import { faCheck } from '@fortawesome/free-solid-svg-icons'
import Fa from 'svelte-fa'
export let feature: any
</script>
<div
class="flex flex-col items-center prose bg-neutral text-neutral-content rounded-lg relative w-full"
>
<div class="text-2xl text-white pt-2">
{feature.title}
</div>
{#if feature.img}
<enhanced:img src={feature.img} class="pl-5 pr-5" />
{/if}
<div class="text-xl p-5">{@html feature.description}</div>
<div
class="absolute top-0 left-0 text-primary-content text-2xl bg-primary p-2 rounded-br-lg rounded-tl-lg"
>
<Fa icon={faCheck} class="" />
</div>
</div>

View File

@ -0,0 +1,13 @@
<script lang="ts">
import FounderCard from '$components/FounderCard.svelte'
import FlounderCard from '$components/FlounderCard.svelte'
</script>
<div class="flex-1 p-5 flex flex-col">
<div class="text-3xl text-center pb-5">Rare deals</div>
<div class="flex flex-wrap gap-6 justify-center">
<FounderCard />
<FlounderCard />
</div>
</div>

View File

@ -7,12 +7,14 @@
<td class=" px-4 py-0 text-center">
<div class="relative h-full w-full py-3 flex justify-center">
{#if item === 'YesBlock'}
<Fa icon={faCheck} class="text-primary" />
{:else if item === 'NoBlock'}
<Fa icon={faX} class="text-error" />
{:else}
{@html item}
{/if}
<span>
{#if item === 'YesBlock'}
<Fa icon={faCheck} class="text-primary" />
{:else if item === 'NoBlock'}
<Fa icon={faX} class="text-error" />
{:else}
{@html item}
{/if}
</span>
</div>
</td>

View File

@ -0,0 +1,8 @@
<script lang="ts">
import Card from './Card.svelte'
import { features } from './features'
</script>
{#each features as feature}
<Card {feature} />
{/each}

View File

@ -1,15 +0,0 @@
<script lang="ts">
import FeatureSupportBlock from './FeatureSupportBlock.svelte'
export let feature: string
export let item: string
</script>
<div
class="flex items-center justify-between px-4 py-3 sm:grid sm:grid-cols-2 sm:px-0"
>
<dt class="pr-4">{feature}</dt>
<dd class="flex items-center justify-end sm:justify-center sm:px-4">
<FeatureSupportBlock {item} />
</dd>
</div>

View File

@ -0,0 +1,30 @@
<script lang="ts">
import Card from './Card.svelte'
import Testimonials from '$src/components/Testimonials.svelte'
import CtaButton from './CTAButton.svelte'
import { features } from './features'
import Deals from './Deals.svelte'
import Features from './Features.svelte'
</script>
<div class="prose ml-auto mr-auto">
<p class="text-3xl text-center">PocketHost Access</p>
<p class="text-2xl text-center">
<span class="text-primary">$5/instance</span> or
<span class="text-primary">$25/unlimited</span>
</p>
<p class="text-xl text-accent text-center">7 Day Free Trial</p>
<div class="flex justify-center mt-10 mb-10">
<CtaButton />
</div>
</div>
<div class="flex flex-col gap-10 items-center">
<Features />
</div>
<Deals />
<Testimonials />
<CtaButton fixed />

View File

@ -1,155 +0,0 @@
<script lang="ts">
import FeatureName from '$src/routes/(static)/pricing/FeatureName.svelte'
import FeatureSupportBlock from './FeatureSupportBlock.svelte'
import { PLAN_NAMES, SubscriptionType } from 'pockethost/common'
import { isUserVerified, userStore } from '$src/util/stores'
interface Item {
name: string
items: string[]
isNew?: boolean
info?: string
}
const plans = [
{
name: PLAN_NAMES[SubscriptionType.Free],
price: [
{
text: '$5-25 monthly',
link: `https://store.pockethost.io/buy/d4b2d062-429c-49b4-9cdc-853aaeb17e20?checkout[custom][user_id]=${$userStore?.id}&checkout[email]=${$userStore?.email}`,
},
],
},
]
const items: Item[] = [
{
name: 'Number of Instances',
items: ['$5/instance (up to 4) or $25 for unlimited'],
info: `Each instance can have its own domain, database, and files.`,
},
{
name: 'Premium Bandwidth',
items: ['Unlimited fair use'],
},
{
name: 'Storage',
items: ['Unlimited fair use'],
},
{
name: 'CPU',
items: ['Unlimited fair use'],
},
{
name: 'FTP access',
items: ['YesBlock'],
},
{
name: 'Run every version of PocketBase',
items: ['YesBlock'],
info: `We support the latest patch of every minor release of PocketBase.`,
},
{
name: 'Secure infrastructure',
items: ['YesBlock'],
},
{
name: 'Community Discord',
items: ['YesBlock'],
},
{
name: 'Custom Domains',
items: ['YesBlock'],
},
{ name: 'Pro Discord', items: ['YesBlock'] },
]
</script>
<div class="bg-info text-info-content p-4 text-center text-lg">
Learn more about our <a href="/docs/pricing-ethos" class="link"
>pricing ethos</a
>
and <a href="/terms" class="link">Fair Use</a>.
</div>
<div
class="flex justify-center items-center flex-col space-y-10 p-10 bg-emerald-950"
>
<div class="text-2xl text-center">Features</div>
<table class="hidden md:table border-spacing-x-8 max-w-[354px] md:max-w-md">
<thead class="table-header-group">
<tr class="">
<th class=""></th>
{#each plans as plan}
<th>
<div class="flex flex-col justify-center py-4">
{#each plan.price as pPrice}
<a
href={$userStore && $isUserVerified ? pPrice.link : `/login`}
class="btn btn-sm btn-secondary text-base mb-2"
>
{pPrice.text}
</a>
{/each}
</div>
</th>
{/each}
</tr>
</thead>
<tbody>
{#each items as item}
<tr class="table-row text-base border-b border-gray-700">
<FeatureName {item} enlarge />
{#each item.items as itemVal, idx}
<FeatureSupportBlock item={itemVal ?? ''} />
{/each}
</tr>
{/each}
</tbody>
</table>
<table class="md:hidden border-spacing-x-8 max-w-[354px] md:max-w-md">
<thead class="table-header-group">
{#each plans as plan}
<tr class="text-left">
<th class=" text-lg min-w-48">
{plan.name}
</th>
<th>
<div class="flex flex-col justify-center py-4">
{#each plan.price as pPrice}
<a
href={$userStore && $isUserVerified ? pPrice.link : `/login`}
class="btn btn-xs w-40 btn-primary mb-2"
>
{pPrice.text}
</a>
{/each}
</div>
</th>
</tr>
{/each}
</thead>
<tbody>
{#each items as item}
<tr class="table-row text-base border-b border-gray-700 col-span-2">
<FeatureName {item} enlarge />
</tr>
{#each item.items as itemVal, idx}
<tr class="table-row border-b even:border-0 border-gray-800">
<td class="text-left">
{plans[idx]?.name}
</td>
<FeatureSupportBlock item={itemVal ?? ''} />
</tr>
{/each}
{/each}
</tbody>
</table>
</div>

View File

@ -0,0 +1,83 @@
import regions from './regions.png?enhanced'
export const features = [
{
title: 'Risk-free Trial',
description: `Test-drive PocketHost for 7 days. Credit card required.`,
},
{
title: 'Global Infrastructure',
description:
'PocketHost operates across 40+ regional edge locations. Users connect through our distributed network of edge servers, while our enterprise-grade internal VPN ensures optimal routing to your data. Experience consistent 10-30ms edge latency for superior application performance.',
img: regions,
},
{
title: 'Developer-Friendly Pricing',
description: `Get started for just $5 per instance. After 5 instances, it's free.`,
},
{
title: 'Unlimited Bandwidth, Storage, and CPU',
description:
'Access to unlimited bandwidth, storage, and computational resources under our <a href="/docs/pricing-ethos" class="link">fair use policy</a>.',
},
{
title: 'FTP access',
description: 'Easily access your data from any FTP client.',
},
{
title: 'Run every version of PocketBase',
description: `We support the latest patch of every minor release of PocketBase.`,
},
{
title: 'Enterprise-Grade Security',
description:
'Infrastructure secured with RSA-2048 encryption and industry-standard security protocols.',
},
{
title: 'Community Support',
description:
'Access to our community Discord platform with over 1,000 developers and technical support resources.',
},
{
title: 'Priority Support Channel',
description:
'Subscribers receive access to dedicated support channels via Discord.',
},
{
title: 'Domain Customization',
description:
'Seamless custom domain integration for your PocketHost instances.',
},
{
title: 'Lifetime Access Option',
description:
'Limited-time opportunity for lifetime Pro tier access (up to 250 instances) with a single payment.',
},
{
title: 'Early Access to New Tech',
description: `Get your hands on our experimental <a href="/blog/announcing-pocker" class="link">Pocker</a> tech before anyone else. Help us push the boundaries of what's possible.`,
},
{
title: 'Enterprise Reliability',
description:
'Industry-leading 99.95% uptime guarantee ensures consistent application availability.',
},
{
title: 'Managed Infrastructure',
description:
'Comprehensive infrastructure management including scaling, backups, and maintenance operations.',
},
{
title: 'Hack More, Pay Less',
description:
'Why waste time managing servers? Put your resources into building the next big thing. Your wallet (and your future app) will thank you.',
},
{
title: 'Open Source Support',
description:
'Your subscription directly supports the development of open-source PocketBase and PocketHost projects.',
},
]

View File

Before

Width:  |  Height:  |  Size: 286 KiB

After

Width:  |  Height:  |  Size: 286 KiB