Skip to content

Commit

Permalink
Merge pull request #13 from coinbase/alissa.crane/charges
Browse files Browse the repository at this point in the history
chore: add modal
  • Loading branch information
abcrane123 authored Oct 22, 2024
2 parents a435b57 + cb0c04b commit eb968b8
Show file tree
Hide file tree
Showing 12 changed files with 198 additions and 40 deletions.
Binary file modified bun.lockb
Binary file not shown.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"test:coverage": "vitest run --coverage"
},
"dependencies": {
"@coinbase/onchainkit": "^0.34.1",
"@coinbase/onchainkit": "^0.35.0",
"next": "^14.2.5",
"permissionless": "^0.1.26",
"react": "^18",
Expand Down
25 changes: 25 additions & 0 deletions src/components/MockCheckoutButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { CoinbasePaySvg } from 'src/svg/CoinbasePaySvg';
import type { MockCheckoutButtonReact } from 'src/types';

export function MockCheckoutButton({ onClick }: MockCheckoutButtonReact) {
return (
<div className="w-64">
<div className="default-dark flex w-full flex-col gap-2">
<button
type="button"
onClick={onClick}
className="active:ock-bg-secondary-active ock-border-radius ock-font-family flex w-full cursor-pointer items-center justify-center bg-[#0052FF] px-4 py-3 font-semibold leading-normal hover:bg-[#0045D8]"
>
<div className="flex items-center justify-center whitespace-nowrap">
<div className="mr-2 flex h-5 w-5 shrink-0 items-center justify-center">
<CoinbasePaySvg />
</div>
</div>
<span className="ock-font-family font-semibold text-gray-50 leading-normal">
Pay with Crypto
</span>
</button>
</div>
</div>
);
}
6 changes: 5 additions & 1 deletion src/components/OnchainProviders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ function OnchainProviders({ children }: Props) {
return (
<WagmiProvider config={wagmiConfig}>
<QueryClientProvider client={queryClient}>
<OnchainKitProvider apiKey={NEXT_PUBLIC_CDP_API_KEY} chain={base}>
<OnchainKitProvider
apiKey={NEXT_PUBLIC_CDP_API_KEY}
chain={base}
config={{ appearance: { theme: 'base' } }}
>
{children}
</OnchainKitProvider>
</QueryClientProvider>
Expand Down
7 changes: 5 additions & 2 deletions src/components/OnchainStore.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useState } from 'react';
import { Banner } from './Banner';
import Navbar from './Navbar';
import OnchainStoreCart from './OnchainStoreCart';
Expand All @@ -6,17 +7,19 @@ import { OnchainStoreProvider } from './OnchainStoreProvider';
import OnchainStoreSummary from './OnchainStoreSummary';

export default function OnchainStore() {
const [showModal, setShowModal] = useState(false);

return (
<OnchainStoreProvider>
<div className="flex h-full max-h-screen max-w-full flex-col font-sansMono">
<div className="relative flex h-full max-h-screen max-w-full flex-col font-sansMono">
<Banner />
<Navbar />
<main className="mx-auto flex max-w-5xl grow flex-col pt-[5.5rem] pb-10">
<div className="flex grow flex-col pb-10 md:flex-row">
<OnchainStoreSummary />
<OnchainStoreItems />
</div>
<OnchainStoreCart />
<OnchainStoreCart showModal={showModal} setShowModal={setShowModal} />
</main>
</div>
</OnchainStoreProvider>
Expand Down
95 changes: 62 additions & 33 deletions src/components/OnchainStoreCart.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import { useCallback, useMemo } from 'react';
import { useOnchainStoreContext } from './OnchainStoreProvider';
import { Pay, PayButton } from '@coinbase/onchainkit/pay';
import type { LifecycleStatus } from '@coinbase/onchainkit/pay';
import useCreateCharge from 'src/hooks/useCreateCharge';
// import useCreateCharge from 'src/hooks/useCreateCharge';
// import {
// Checkout,
// CheckoutButton,
// LifecycleStatus,
// } from '@coinbase/onchainkit/checkout';
import type { OnchainStoreCartReact } from 'src/types';
import OnchainStoreModal from './OnchainStoreModal';
import { MockCheckoutButton } from './MockCheckoutButton';

export default function OnchainStoreCart() {
export default function OnchainStoreCart({
setShowModal,
showModal,
}: OnchainStoreCartReact) {
const { quantities, products } = useOnchainStoreContext();

const { createCharge } = useCreateCharge();
// const { createCharge } = useCreateCharge();

const totalSum = useMemo(() => {
return (
Expand All @@ -18,32 +27,46 @@ export default function OnchainStoreCart() {
);
}, [products, quantities]);

const handleStatusChange = useCallback((status: LifecycleStatus) => {
console.log('onStatus', status);
}, []);
// const handleStatusChange = useCallback((status: LifecycleStatus) => {
// console.log('onStatus', status);
// }, []);

const chargeHandler = useCallback(() => {
const description = Object.keys(quantities)
.map((productId) => {
return `${productId}(${quantities[productId]})`;
})
.join(',');
const chargeDetails = {
name: 'commerce template charge',
description,
pricing_type: 'fixed_price',
local_price: {
// NOTE: The values are set to zero on the template as we're not actually looking to sell anything here.
// To test with real values, replace with totalSum.toString() in real app
amount: '0',
currency: 'USD',
},
};
return createCharge(chargeDetails);
}, [createCharge, quantities]);
// const chargeHandler = useCallback(() => {
// const description = Object.keys(quantities)
// .map((productId) => {
// return `${productId}(${quantities[productId]})`;
// })
// .join(',');
// const chargeDetails = {
// name: 'commerce template charge',
// description,
// pricing_type: 'fixed_price',
// local_price: {
// amount: totalSum.toString(),
// currency: 'USD',
// },
// };
// return createCharge(chargeDetails);
// }, [createCharge, quantities, totalSum]);

// const key = useMemo(() => {
// if (!quantities) return '';
// const productIds = Object.keys(quantities);
// const values = Object.values(quantities).flat();
// return `${productIds.join('.')}-${values.join('.')}`;
// }, [quantities]);

const closeModal = useCallback(() => {
setShowModal?.(false);
}, [setShowModal]);

const openModal = useCallback(() => {
setShowModal?.(true);
}, [setShowModal]);

return (
<div className="-mx-[50vw] fixed right-1/2 bottom-0 left-1/2 w-screen border-gray-200 border-t bg-[white]">
{showModal && <OnchainStoreModal closeModal={closeModal} />}
<div className="mx-auto max-w-5xl ">
<div className="flex flex-col items-start justify-between py-4 md:flex-row md:items-center">
<span className="mb-2 hidden px-4 text-xs sm:flex md:mb-0 md:w-1/3 lg:px-6">
Expand All @@ -54,15 +77,21 @@ export default function OnchainStoreCart() {
TOTAL {totalSum.toFixed(2)} USDC
</h2>
<div className="w-64">
<Pay onStatus={handleStatusChange} chargeHandler={chargeHandler}>
<PayButton
{/* TODO: comment back in Checkout component in live environment */}
{/* <Checkout
key={key}
onStatus={handleStatusChange}
chargeHandler={chargeHandler}
>
<CheckoutButton
coinbaseBranded={true}
text="Pay with Crypto"
className="mt-0"
// NOTE: comment back in to disable $0 amount in real app
// disabled={!totalSum}
disabled={!totalSum}
/>
</Pay>
</Checkout> */}

{/* TODO: remove, for demo purposes only */}
<MockCheckoutButton onClick={openModal} />
</div>
</div>
</div>
Expand Down
44 changes: 44 additions & 0 deletions src/components/OnchainStoreModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Image from 'next/image';
import CommerceScreenImage from '../images/commerceScreen.png';
import type { OnchainStoreModalReact } from 'src/types';
import { GITHUB_LINK } from 'src/links';
import { CloseSvg } from 'src/svg/CloseSvg';

export default function OnchainStoreModal({
closeModal,
}: OnchainStoreModalReact) {
return (
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
<div className="relative z-10 flex h-full xs:h-auto max-w-lg flex-col gap-2 xs:rounded-[10px] bg-[white] p-6 px-10">
<button
type="button"
className="absolute top-2 right-4"
onClick={closeModal}
>
<CloseSvg />
</button>
<div className="flex flex-col items-start gap-2 pt-4 pb-4">
<div className="font-bold">Try it locally</div>
<span className="text-sm ">
<a href={GITHUB_LINK} className="ock-text-primary">
Fork the template and experience the end-to-end checkout flow.{' '}
</a>
Your users will see the below screen when the payment flow is
active.
</span>
<div className="mx-auto flex grow justify-center py-4">
<Image
src={CommerceScreenImage}
alt="123"
className="mx-auto h-[400px] w-auto rounded-[10px]"
/>
</div>
<div className="ock-text-foreground-muted text-xs ">
These products are not for sale. We have disabled the end-to-end
checkout flow on production.
</div>
</div>
</div>
</div>
);
}
Binary file added src/images/commerceScreen.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions src/svg/CloseSvg.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export function CloseSvg() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="size-6"
>
<title>CloseSvg</title>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M6 18 18 6M6 6l12 12"
/>
</svg>
);
}
21 changes: 21 additions & 0 deletions src/svg/CoinbasePaySvg.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export function CoinbasePaySvg() {
return (
<svg
role="img"
aria-label="ock-coinbasePaySvg"
width="100%"
height="100%"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<title>CoinbasePaySvg</title>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M10.0145 14.1666C7.82346 14.1666 6.04878 12.302 6.04878 9.99996C6.04878 7.69788 7.82346 5.83329 10.0145 5.83329C11.9776 5.83329 13.6069 7.33677 13.9208 9.30552H17.9163C17.5793 5.02774 14.172 1.66663 10.0145 1.66663C5.63568 1.66663 2.08301 5.39926 2.08301 9.99996C2.08301 14.6007 5.63568 18.3333 10.0145 18.3333C14.172 18.3333 17.5793 14.9722 17.9163 10.6944H13.9208C13.6069 12.6632 11.9776 14.1666 10.0145 14.1666Z"
fill="#f9fafb"
/>
</svg>
);
}
6 changes: 3 additions & 3 deletions src/svg/ExternalLinkSvg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ export function ExternalLinkSvg() {
<path
d="M6 14L14 6M14 6H8M14 6V12"
stroke="black"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
Expand Down
13 changes: 13 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,16 @@ export type QuantityInputButtonReact = {
svg: ReactNode;
label: string;
};

export type OnchainStoreCartReact = {
setShowModal?: (value: boolean) => void;
showModal?: boolean;
};

export type OnchainStoreModalReact = {
closeModal: () => void;
};

export type MockCheckoutButtonReact = {
onClick: () => void;
};

0 comments on commit eb968b8

Please sign in to comment.