Skip to content

Commit

Permalink
feat: update pricing page
Browse files Browse the repository at this point in the history
  • Loading branch information
KayBeSee committed Nov 15, 2022
1 parent 46fb930 commit 1784829
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 53 deletions.
73 changes: 33 additions & 40 deletions apps/frontend/src/components/AlertBar.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
import React, { useContext, useState, useEffect } from 'react';
import { blockExplorerTransactionURL } from 'unchained-bitcoin';
import { useHistory } from 'react-router-dom';
import { ExclamationIcon } from '@heroicons/react/outline';
import { ExternalLinkIcon } from '@heroicons/react/outline';

import { AccountMapContext, ConfigContext, PlatformContext } from 'src/context';

import { getLicenseBannerMessage, licenseTxId } from 'src/utils/license';
import { getUnchainedNetworkFromBjslibNetwork } from 'src/utils/files';

import { OnChainConfig, VaultConfig } from '@lily/types';

interface Props {
accountConfig: OnChainConfig;
config: OnChainConfig;
}

export const AlertBar = ({ accountConfig }: Props) => {
const { config, nodeConfig, currentBitcoinNetwork } = useContext(ConfigContext);
export const AlertBar = ({ config }: Props) => {
const { nodeConfig, currentBitcoinNetwork } = useContext(ConfigContext);
const { platform } = useContext(PlatformContext);
const { setCurrentAccountId } = useContext(AccountMapContext);
const history = useHistory();
Expand All @@ -25,11 +23,11 @@ export const AlertBar = ({ accountConfig }: Props) => {

useEffect(() => {
async function checkLicenseTxConfirmed() {
if ((accountConfig as VaultConfig).license) {
if ((config as VaultConfig).license) {
let licenseTxConfirmed = false;
if (nodeConfig) {
try {
const txId = licenseTxId((accountConfig as VaultConfig).license);
const txId = licenseTxId((config as VaultConfig).license);
if (txId) {
licenseTxConfirmed = await platform.isConfirmedTransaction(txId);
}
Expand All @@ -38,7 +36,7 @@ export const AlertBar = ({ accountConfig }: Props) => {
console.log('AlertBar: Error retrieving license transaction');
}
const { message, promptBuy } = getLicenseBannerMessage(
accountConfig as VaultConfig,
config as VaultConfig,
licenseTxConfirmed,
nodeConfig
);
Expand All @@ -51,44 +49,39 @@ export const AlertBar = ({ accountConfig }: Props) => {
checkLicenseTxConfirmed();
}, [config, nodeConfig, platform]);

if (buyMessage && !!(accountConfig as VaultConfig).license) {
if (buyMessage && !!(config as VaultConfig).license) {
return (
<div className='bg-yellow-100 z-10 rounded-2xl py-6 my-6 shadow border border-yellow-600/20'>
<div className='relative bg-yellow-100 z-10 rounded-2xl py-6 my-6 shadow border border-yellow-600/20'>
<div className='max-w-7xl mx-auto py-3 px-3 sm:px-6 lg:px-8'>
<div className='flex items-center justify-between flex-wrap'>
<div className='w-full sm:w-0 flex-1 flex items-center'>
<span className='flex p-2 rounded-lg bg-yellow-300'>
<ExclamationIcon className='h-6 w-6 text-yellow-800' aria-hidden='true' />
</span>
<p className='ml-3 font-medium text-yellow-800 truncate'>
{/* TODO: change later */}
<span className='md:hidden'>Upgrade your account!</span>
<span className='hidden md:inline'>{buyMessage}</span>
</p>
</div>
<div className='mt-2 flex-shrink-0 w-full sm:mt-0 sm:w-auto'>
{showBuyButton ? (
<a
onClick={() => {
setCurrentAccountId(accountConfig.id);
history.push(`/vault/${accountConfig.id}/purchase`);
}}
className='cursor-pointer flex items-center justify-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-yellow-600 bg-white hover:bg-yellow-50'
>
Upgrade
</a>
) : (
<h3 className='font-medium text-yellow-800 truncate'>
Thank you for using Lily Wallet!
</h3>
<p className='font-base text-yellow-800 truncate'>{buyMessage}</p>
<div className='mt-4'>
<div className='-mx-2 -my-1.5 flex space-x-2'>
{showBuyButton ? (
<button
onClick={() => {
setCurrentAccountId(config.id);
history.push(`/vault/${config.id}/purchase`);
}}
className='cursor-pointer flex items-center justify-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-yellow-600 bg-white hover:bg-yellow-50'
>
Purchase a license
</button>
) : null}

<a
href={blockExplorerTransactionURL(
licenseTxId((accountConfig as VaultConfig).license)!,
getUnchainedNetworkFromBjslibNetwork(currentBitcoinNetwork)
)}
href='https://docs.lily-wallet.com/license'
target='_blank'
className='flex items-center justify-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-yellow-600 bg-white hover:bg-yellow-50'
type='button'
className='flex items-center flex-nowrap rounded-md bg-yellow-100 px-2 py-1.5 text-sm font-medium text-yellow-800 hover:bg-yellow-200 focus:outline-none focus:ring-2 focus:ring-yellow-600 focus:ring-offset-2 focus:ring-offset-yellow-50'
>
View Transaction
What is this?
<ExternalLinkIcon className='ml-1 w-3- h-3' />
</a>
)}
</div>
</div>
</div>
</div>
Expand Down
191 changes: 191 additions & 0 deletions apps/frontend/src/components/PricingChart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import React, { useState } from 'react';
import { CheckIcon } from '@heroicons/react/outline';
import { Transition } from '@headlessui/react';

import { LicenseTiers, LilyOnchainAccount } from '@lily/types';

const tiers = [
{
name: 'Multisig',
href: '/download',
priceAnnual: 100,
description: 'A premium solution for businesses, inheritance, or high net-worth individuals',
features: [
'Eliminate single points of failure',
'Collaborative custody with other stakeholders',
'Zero KYC or invasive surveillance',
'Premium support staff on call to assist'
],
cta: 'Purchase'
}
];

interface Props {
clickRenewLicense: (tier: LicenseTiers, currentAccount: LilyOnchainAccount) => Promise<void>;
currentAccount: LilyOnchainAccount;
}

export const PricingChart = ({ clickRenewLicense, currentAccount }: Props) => {
const [isLoading, setLoading] = useState('');

const onLicenseClick = async (tier: LicenseTiers, account: LilyOnchainAccount) => {
if (!isLoading) {
setLoading(tier);
try {
await clickRenewLicense(tier, account);
setLoading('');
} catch (e) {
setLoading('');
}
}
};

return (
<>
<div className='relative'>
<div className=''>
<div className='pt-12 sm:pt-16 bg-emerald-600 bg-gradient-to-b from-emerald-500 to-emerald-700'>
<div className='mx-auto max-w-7xl px-4 text-center sm:px-6 lg:px-8'>
<div className='mx-auto max-w-3xl space-y-2 pb-8 sm:pb-12 lg:pb-16'>
<Transition
appear={true}
show={true}
enter='transition-all duration-500'
enterFrom='opacity-0 translate-y-6'
enterTo='opacity-100 translate-y-0'
leave='transition-opacity duration-150'
leaveFrom='opacity-100'
leaveTo='opacity-0'
>
<p className='text-3xl font-bold tracking-tight text-white sm:text-4xl lg:text-5xl'>
Pricing
</p>
</Transition>
<Transition
appear={true}
show={true}
enter='transition-all duration-500 delay-100'
enterFrom='opacity-0 translate-y-6'
enterTo='opacity-100 translate-y-0'
leave='transition-opacity duration-150'
leaveFrom='opacity-100'
leaveTo='opacity-0'
>
<p className='text-xl text-green-200'>
Upgrade to multisig for more robust security and complex custody solutions.
</p>
</Transition>
</div>
</div>
</div>
<div className=''>
<div className='relative'>
<div className='absolute inset-0 h-3/4 bg-emerald-700' />
<div className='relative z-10 mx-auto max-w-7xl px-4 sm:px-6 lg:px-8'>
<div className='mx-auto max-w-md space-y-4 lg:grid lg:max-w-5xl lg:grid-cols-2 lg:gap-5 lg:space-y-0'>
<div
key={tiers[0].name}
className='flex flex-col overflow-hidden rounded-3xl shadow-lg'
>
<div className='bg-white px-6 py-8 sm:p-10 sm:pb-6'>
<div>
<h3
className='inline-flex text-lg font-medium font-sans text-transparent text-yellow-400 bg-clip-text bg-gradient-to-b from-yellow-400 to-yellow-500'
id='tier-standard'
>
{tiers[0].name}
</h3>
</div>
{tiers[0].priceAnnual ? (
<div className='mt-1 flex items-baseline text-6xl font-bold tracking-tight'>
${tiers[0].priceAnnual}
<span className='ml-1 text-2xl font-medium tracking-normal text-gray-500'>
/year
</span>
</div>
) : (
<div className='mt-1 flex items-baseline text-6xl font-bold tracking-tight'>
Free
</div>
)}
<p className='mt-5 text-lg text-gray-500'>{tiers[0].description}</p>
</div>
<div className='flex flex-1 flex-col justify-between space-y-16 bg-gray-50 px-6 pt-6 pb-8 sm:p-10 sm:pt-12'>
<ul role='list' className='space-y-8'>
{tiers[0].features.map((feature) => (
<li key={feature} className='flex items-start'>
<div className='flex-shrink-0'>
<CheckIcon className='h-6 w-6 text-green-500' aria-hidden='true' />
</div>
<p className='ml-3 text-base text-gray-700'>{feature}</p>
</li>
))}
</ul>
<div className='rounded-md shadow'>
<button
onClick={() => onLicenseClick(LicenseTiers.basic, currentAccount)}
className='w-full flex items-center justify-center rounded-md border border-transparent bg-yellow-500 bg-gradient-to-b from-yellow-400 to-yellow-500 hover:from-yellow-300 hover:to-yellow-400 px-5 py-3 text-base font-medium text-white hover:bg-yellow-400'
aria-describedby='tier-standard'
>
{!isLoading ? tiers[0].cta : 'Creating transaction...'}
</button>
</div>
</div>
</div>

<div
key={tiers[0].name}
className='flex flex-col overflow-hidden rounded-3xl shadow-lg'
>
<div className='bg-white px-6 py-8 sm:p-10 sm:pb-6'>
<h2 className='text-2xl font-extrabold text-gray-900 mb-8'>
Frequently asked questions
</h2>
<div className='space-y-16'>
<div className='space-y-2'>
<dt className='text-lg leading-6 font-medium text-gray-900'>
Why do your charge money for multisig security?
</dt>
<dd className='text-base text-gray-500'>
We assume that users requiring multisig security are holding large
amounts of bitcoin or have a complex custodial arrangement.
</dd>
<dd className='text-base text-gray-500'>
We ask users who use multisig to purchase a license to help support
development of Lily Wallet.
</dd>
</div>

<div className='space-y-2'>
<dt className='text-lg leading-6 font-medium text-gray-900'>
Do you require KYC/AML information for a license?
</dt>
<dd className='text-base text-gray-500'>
Purchasing a license doesn't require any KYC/AML information from our
customers.
</dd>
</div>
</div>
</div>
<div className='flex flex-1 flex-col justify-between space-y-16 bg-gray-50 px-6 pt-6 pb-8 sm:p-10 sm:pt-12'>
<div className='rounded-md shadow bg-white'>
<a
href='https://lily-wallet.com/pricing'
target='_blank'
className='flex items-center justify-center rounded-md border border-transparent px-5 py-3 text-base font-medium'
aria-describedby='tier-standard'
>
Learn more
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</>
);
};
1 change: 1 addition & 0 deletions apps/frontend/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export * from './MnemonicWordsDisplayer';
export * from './NavLinks';
export * from './NoAccountsEmptyState';
export * from './OutsideClick';
export * from './PricingChart';
export * from './PricingTable';
export * from './PromptPinModal';
export * from './Price';
Expand Down
2 changes: 1 addition & 1 deletion apps/frontend/src/components/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const PageWrapper = ({ children }: Props) => {
</div>
</div>
)}
<main className='flex-1 z-10 dark:bg-gray-900 bg-gray-100 relative'>
<main className='flex-1 z-10 dark:bg-gray-900 relative'>
<ColorOverlap className='bg-green-700 dark:bg-green-900' style={{ zIndex: '-1' }} />
<div className='max-w-7xl mx-auto px-4 sm:px-6 md:px-16 py-8'>{children}</div>
</main>
Expand Down
21 changes: 15 additions & 6 deletions apps/frontend/src/pages/Purchase/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import BigNumber from 'bignumber.js';
import { ShieldCheckIcon } from '@heroicons/react/solid';

import {
PricingChart,
PricingTable,
PurchaseLicenseSuccess,
ErrorModal,
Expand Down Expand Up @@ -208,7 +209,7 @@ const PurchasePage = ({
return (
<PageWrapper>
<>
<Header>
{/* <Header>
<HeaderLeft>
<PageTitle>Purchase a license</PageTitle>
</HeaderLeft>
Expand All @@ -222,12 +223,20 @@ const PurchasePage = ({
Questions? Click here for support.
</RenewButton>
</Buttons>
</Header>
{step === 0 && (
<PricingTable clickRenewLicense={clickRenewLicense} currentAccount={currentAccount} />
)}
</Header> */}
{
step === 0 && (
<PricingChart clickRenewLicense={clickRenewLicense} currentAccount={currentAccount} />
)
// <PricingTable clickRenewLicense={clickRenewLicense} currentAccount={currentAccount} />
}
{step === 1 && licenseResponse && (
<>
<Header>
<HeaderLeft>
<PageTitle>Checkout</PageTitle>
</HeaderLeft>
</Header>
{finalPsbt && (
<ConfirmTxPage
currentAccount={currentAccount}
Expand All @@ -245,7 +254,7 @@ const PurchasePage = ({
<ShieldCheckIcon className='w-12 h-12 text-green-500 dark:text-green-400' />
</div>
),
header: `License for Lily Wallet (${capitalize(licenseTier(licenseResponse))})`,
header: `License for Lily Wallet`,
subtext: getValue(finalPsbt.txOutputs[0].value),
extraInfo: [
{
Expand Down
Loading

0 comments on commit 1784829

Please sign in to comment.