diff --git a/components/cart/CartItem/CartItem.tsx b/components/cart/CartItem/CartItem.tsx index 4453e0c08..334761b54 100644 --- a/components/cart/CartItem/CartItem.tsx +++ b/components/cart/CartItem/CartItem.tsx @@ -85,25 +85,29 @@ const CartItem = ({
- closeSidebarIfPresent()} - className={s.productImage} - width={150} - height={150} - src={item.variant.image!.url} - alt={item.variant.image!.altText} - unoptimized - /> + + closeSidebarIfPresent()} + className={s.productImage} + width={150} + height={150} + src={item.variant.image!.url} + alt={item.variant.image!.altText} + unoptimized + /> +
- closeSidebarIfPresent()} - > - {item.name} - + + closeSidebarIfPresent()} + > + {item.name} + + {options && options.length > 0 && (
diff --git a/components/cart/CartSidebarView/CartSidebarView.tsx b/components/cart/CartSidebarView/CartSidebarView.tsx index 128b928a8..2fa7980f2 100644 --- a/components/cart/CartSidebarView/CartSidebarView.tsx +++ b/components/cart/CartSidebarView/CartSidebarView.tsx @@ -74,9 +74,11 @@ const CartSidebarView: FC = () => { <>
- - My Cart - + + + My Cart + +
    {data!.lineItems.map((item: any) => ( diff --git a/components/checkout/CheckoutSidebarView/CheckoutSidebarView.tsx b/components/checkout/CheckoutSidebarView/CheckoutSidebarView.tsx index fb562e7af..9d8661bfa 100644 --- a/components/checkout/CheckoutSidebarView/CheckoutSidebarView.tsx +++ b/components/checkout/CheckoutSidebarView/CheckoutSidebarView.tsx @@ -1,30 +1,39 @@ -import cn from 'classnames' import Link from 'next/link' import { FC } from 'react' import CartItem from '@components/cart/CartItem' import { Button, Text } from '@components/ui' import { useUI } from '@components/ui/context' +import SidebarLayout from '@components/common/SidebarLayout' import useCart from '@framework/cart/use-cart' import usePrice from '@framework/product/use-price' +import useCheckout from '@framework/checkout/use-checkout' import ShippingWidget from '../ShippingWidget' import PaymentWidget from '../PaymentWidget' -import SidebarLayout from '@components/common/SidebarLayout' import s from './CheckoutSidebarView.module.css' const CheckoutSidebarView: FC = () => { - const { setSidebarView } = useUI() - const { data } = useCart() + const { setSidebarView, closeSidebar } = useUI() + const { data: cartData } = useCart() + const { data: checkoutData, submit: onCheckout } = useCheckout() + + async function handleSubmit(event: React.ChangeEvent) { + event.preventDefault() + + await onCheckout() + + closeSidebar() + } const { price: subTotal } = usePrice( - data && { - amount: Number(data.subtotalPrice), - currencyCode: data.currency.code, + cartData && { + amount: Number(cartData.subtotalPrice), + currencyCode: cartData.currency.code, } ) const { price: total } = usePrice( - data && { - amount: Number(data.totalPrice), - currencyCode: data.currency.code, + cartData && { + amount: Number(cartData.totalPrice), + currencyCode: cartData.currency.code, } ) @@ -35,25 +44,36 @@ const CheckoutSidebarView: FC = () => { >
    - Checkout + + Checkout + - setSidebarView('PAYMENT_VIEW')} /> - setSidebarView('SHIPPING_VIEW')} /> + setSidebarView('PAYMENT_VIEW')} + /> + setSidebarView('SHIPPING_VIEW')} + />
      - {data!.lineItems.map((item: any) => ( + {cartData!.lineItems.map((item: any) => ( ))}
    -
    +
    • Subtotal @@ -74,14 +94,15 @@ const CheckoutSidebarView: FC = () => {
    {/* Once data is correcly filled */} - {/* */} -
    -
+ ) } diff --git a/components/checkout/PaymentMethodView/PaymentMethodView.tsx b/components/checkout/PaymentMethodView/PaymentMethodView.tsx index a5f6f4b51..972073756 100644 --- a/components/checkout/PaymentMethodView/PaymentMethodView.tsx +++ b/components/checkout/PaymentMethodView/PaymentMethodView.tsx @@ -1,83 +1,129 @@ import { FC } from 'react' import cn from 'classnames' + +import useAddCard from '@framework/customer/card/use-add-item' import { Button, Text } from '@components/ui' import { useUI } from '@components/ui/context' -import s from './PaymentMethodView.module.css' import SidebarLayout from '@components/common/SidebarLayout' +import s from './PaymentMethodView.module.css' + +interface Form extends HTMLFormElement { + cardHolder: HTMLInputElement + cardNumber: HTMLInputElement + cardExpireDate: HTMLInputElement + cardCvc: HTMLInputElement + firstName: HTMLInputElement + lastName: HTMLInputElement + company: HTMLInputElement + streetNumber: HTMLInputElement + zipCode: HTMLInputElement + city: HTMLInputElement + country: HTMLSelectElement +} + const PaymentMethodView: FC = () => { const { setSidebarView } = useUI() + const addCard = useAddCard() + + async function handleSubmit(event: React.ChangeEvent
) { + event.preventDefault() + + await addCard({ + cardHolder: event.target.cardHolder.value, + cardNumber: event.target.cardNumber.value, + cardExpireDate: event.target.cardExpireDate.value, + cardCvc: event.target.cardCvc.value, + firstName: event.target.firstName.value, + lastName: event.target.lastName.value, + company: event.target.company.value, + streetNumber: event.target.streetNumber.value, + zipCode: event.target.zipCode.value, + city: event.target.city.value, + country: event.target.country.value, + }) + + setSidebarView('CHECKOUT_VIEW') + } return ( - setSidebarView('CHECKOUT_VIEW')}> -
- Payment Method -
-
- - -
-
-
- - + + setSidebarView('CHECKOUT_VIEW')}> +
+ Payment Method +
+
+ +
-
- - +
+
+ + +
+
+ + +
+
+ + +
-
- - +
+
+
+ + +
+
+ + +
-
-
-
-
- - +
+ +
-
- - +
+ +
-
-
- - -
-
- - -
-
- - -
-
-
- - +
+ +
-
- - +
+
+ + +
+
+ + +
+
+
+ +
-
-
- -
-
-
- -
- +
+ +
+ + ) } diff --git a/components/checkout/PaymentWidget/PaymentWidget.tsx b/components/checkout/PaymentWidget/PaymentWidget.tsx index e1892934e..9b496bb44 100644 --- a/components/checkout/PaymentWidget/PaymentWidget.tsx +++ b/components/checkout/PaymentWidget/PaymentWidget.tsx @@ -1,14 +1,15 @@ import { FC } from 'react' import s from './PaymentWidget.module.css' -import { ChevronRight, CreditCard } from '@components/icons' +import { ChevronRight, CreditCard, Check } from '@components/icons' interface ComponentProps { onClick?: () => any + isValid?: boolean } -const PaymentWidget: FC = ({ onClick }) => { - /* Shipping Address - Only available with checkout set to true - +const PaymentWidget: FC = ({ onClick, isValid }) => { + /* Shipping Address + Only available with checkout set to true - This means that the provider does offer checkout functionality. */ return (
@@ -19,9 +20,7 @@ const PaymentWidget: FC = ({ onClick }) => { {/* VISA #### #### #### 2345 */}
-
- -
+
{isValid ? : }
) } diff --git a/components/checkout/ShippingView/ShippingView.tsx b/components/checkout/ShippingView/ShippingView.tsx index 1d03a2aac..ed82bc8a2 100644 --- a/components/checkout/ShippingView/ShippingView.tsx +++ b/components/checkout/ShippingView/ShippingView.tsx @@ -1,77 +1,117 @@ import { FC } from 'react' import cn from 'classnames' -import s from './ShippingView.module.css' + import Button from '@components/ui/Button' import { useUI } from '@components/ui/context' import SidebarLayout from '@components/common/SidebarLayout' +import useAddAddress from '@framework/customer/address/use-add-item' + +import s from './ShippingView.module.css' + +interface Form extends HTMLFormElement { + cardHolder: HTMLInputElement + cardNumber: HTMLInputElement + cardExpireDate: HTMLInputElement + cardCvc: HTMLInputElement + firstName: HTMLInputElement + lastName: HTMLInputElement + company: HTMLInputElement + streetNumber: HTMLInputElement + zipCode: HTMLInputElement + city: HTMLInputElement + country: HTMLSelectElement +} const PaymentMethodView: FC = () => { const { setSidebarView } = useUI() + const addAddress = useAddAddress() + + async function handleSubmit(event: React.ChangeEvent
) { + event.preventDefault() + + await addAddress({ + type: event.target.type.value, + firstName: event.target.firstName.value, + lastName: event.target.lastName.value, + company: event.target.company.value, + streetNumber: event.target.streetNumber.value, + apartments: event.target.streetNumber.value, + zipCode: event.target.zipCode.value, + city: event.target.city.value, + country: event.target.country.value, + }) + + setSidebarView('CHECKOUT_VIEW') + } return ( - setSidebarView('CHECKOUT_VIEW')}> -
-

- Shipping -

-
-
- - Same as billing address -
-
- - - Use a different shipping address - -
-
-
-
- - + + setSidebarView('CHECKOUT_VIEW')}> +
+

+ Shipping +

+
+
+ + Same as billing address
-
- - +
+ + + Use a different shipping address +
-
-
- - -
-
- - -
-
- - -
-
-
- - +
+
+
+ + +
+
+ + +
-
- - +
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ +
-
-
- -
-
-
- -
- +
+ +
+ + ) } diff --git a/components/checkout/ShippingWidget/ShippingWidget.tsx b/components/checkout/ShippingWidget/ShippingWidget.tsx index b072178b0..557a69a8b 100644 --- a/components/checkout/ShippingWidget/ShippingWidget.tsx +++ b/components/checkout/ShippingWidget/ShippingWidget.tsx @@ -1,15 +1,16 @@ import { FC } from 'react' import s from './ShippingWidget.module.css' -import { ChevronRight, MapPin } from '@components/icons' +import { ChevronRight, MapPin, Check } from '@components/icons' import cn from 'classnames' interface ComponentProps { onClick?: () => any + isValid?: boolean } -const ShippingWidget: FC = ({ onClick }) => { - /* Shipping Address - Only available with checkout set to true - +const ShippingWidget: FC = ({ onClick, isValid }) => { + /* Shipping Address + Only available with checkout set to true - This means that the provider does offer checkout functionality. */ return (
@@ -23,9 +24,7 @@ const ShippingWidget: FC = ({ onClick }) => { San Franssisco, California */}
-
- -
+
{isValid ? : }
) } diff --git a/components/product/ProductView/ProductView.tsx b/components/product/ProductView/ProductView.tsx index f689030d6..5c85eb084 100644 --- a/components/product/ProductView/ProductView.tsx +++ b/components/product/ProductView/ProductView.tsx @@ -58,7 +58,7 @@ const ProductView: FC = ({ product, relatedProducts }) => { )}
- +

diff --git a/components/ui/Text/Text.module.css b/components/ui/Text/Text.module.css index f572fdf18..b3bc8342b 100644 --- a/components/ui/Text/Text.module.css +++ b/components/ui/Text/Text.module.css @@ -13,3 +13,64 @@ .sectionHeading { @apply pt-1 pb-2 text-2xl font-bold tracking-wide cursor-pointer mb-2; } + + +/* Apply base font sizes and styles for typography markup (h2, h2, ul, p, etc.). + A helpful addition for whenn page content is consumed from a source managed through a wysiwyg editor. */ + +.body :is(h1, h2, h3, h4, h5, h6, p, ul, ol) { + @apply mb-4; +} + +.body :is(h1, h2, h3, h4, h5, h6):not(:first-child) { + @apply mt-8; +} + +.body :is(h1, h2, h3, h4, h5, h6) { + @apply font-semibold tracking-wide; +} + +.body h1 { + @apply text-5xl; +} + +.body h2 { + @apply text-4xl; +} + +.body h3 { + @apply text-3xl; +} + +.body h4 { + @apply text-2xl; +} + +.body h5 { + @apply text-xl; +} + +.body h6 { + @apply text-lg; +} + +.body ul, +.body ol { + @apply pl-6; +} + +.body ul { + @apply list-disc; +} + +.body ol { + @apply list-decimal; +} + +.body a { + @apply underline; +} + +.body a:hover { + @apply no-underline; +} diff --git a/framework/bigcommerce/api/endpoints/checkout/index.ts b/framework/bigcommerce/api/endpoints/checkout/index.ts index eaba32e47..99263bdb7 100644 --- a/framework/bigcommerce/api/endpoints/checkout/index.ts +++ b/framework/bigcommerce/api/endpoints/checkout/index.ts @@ -2,13 +2,13 @@ import { GetAPISchema, createEndpoint } from '@commerce/api' import checkoutEndpoint from '@commerce/api/endpoints/checkout' import type { CheckoutSchema } from '../../../types/checkout' import type { BigcommerceAPI } from '../..' -import checkout from './checkout' +import submitCheckout from './submit-checkout' export type CheckoutAPI = GetAPISchema export type CheckoutEndpoint = CheckoutAPI['endpoint'] -export const handlers: CheckoutEndpoint['handlers'] = { checkout } +export const handlers: CheckoutEndpoint['handlers'] = { submitCheckout } const checkoutApi = createEndpoint({ handler: checkoutEndpoint, diff --git a/framework/bigcommerce/api/endpoints/checkout/checkout.ts b/framework/bigcommerce/api/endpoints/checkout/submit-checkout.ts similarity index 95% rename from framework/bigcommerce/api/endpoints/checkout/checkout.ts rename to framework/bigcommerce/api/endpoints/checkout/submit-checkout.ts index 15c834557..5c135df36 100644 --- a/framework/bigcommerce/api/endpoints/checkout/checkout.ts +++ b/framework/bigcommerce/api/endpoints/checkout/submit-checkout.ts @@ -5,7 +5,7 @@ import { uuid } from 'uuidv4' const fullCheckout = true -const checkout: CheckoutEndpoint['handlers']['checkout'] = async ({ +const submitCheckout: CheckoutEndpoint['handlers']['submitCheckout'] = async ({ req, res, config, @@ -87,4 +87,4 @@ const checkout: CheckoutEndpoint['handlers']['checkout'] = async ({ res.end() } -export default checkout +export default submitCheckout diff --git a/framework/bigcommerce/api/endpoints/customer/address.ts b/framework/bigcommerce/api/endpoints/customer/address.ts new file mode 100644 index 000000000..491bf0ac9 --- /dev/null +++ b/framework/bigcommerce/api/endpoints/customer/address.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/bigcommerce/api/endpoints/customer/card.ts b/framework/bigcommerce/api/endpoints/customer/card.ts new file mode 100644 index 000000000..491bf0ac9 --- /dev/null +++ b/framework/bigcommerce/api/endpoints/customer/card.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/bigcommerce/api/operations/get-customer-wishlist.ts b/framework/bigcommerce/api/operations/get-customer-wishlist.ts index fc9487ffe..5227ee663 100644 --- a/framework/bigcommerce/api/operations/get-customer-wishlist.ts +++ b/framework/bigcommerce/api/operations/get-customer-wishlist.ts @@ -53,7 +53,7 @@ export default function getCustomerWishlistOperation({ if (ids?.length) { const graphqlData = await commerce.getAllProducts({ - variables: { first: 100, ids }, + variables: { first: 50, ids }, config, }) // Put the products in an object that we can use to get them by id diff --git a/framework/bigcommerce/checkout/use-checkout.tsx b/framework/bigcommerce/checkout/use-checkout.tsx new file mode 100644 index 000000000..942f85b83 --- /dev/null +++ b/framework/bigcommerce/checkout/use-checkout.tsx @@ -0,0 +1,14 @@ +import { SWRHook } from '@commerce/utils/types' +import useCheckout, { UseCheckout } from '@commerce/checkout/use-checkout' + +export default useCheckout as UseCheckout + +export const handler: SWRHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ useData }) => + async (input) => ({}), +} diff --git a/framework/bigcommerce/customer/address/use-add-item.tsx b/framework/bigcommerce/customer/address/use-add-item.tsx new file mode 100644 index 000000000..ac9dcd5cf --- /dev/null +++ b/framework/bigcommerce/customer/address/use-add-item.tsx @@ -0,0 +1,15 @@ +import useAddItem, { UseAddItem } from '@commerce/customer/address/use-add-item' +import { MutationHook } from '@commerce/utils/types' + +export default useAddItem as UseAddItem + +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ fetch }) => + () => + async () => ({}), +} diff --git a/framework/bigcommerce/customer/card/use-add-item.tsx b/framework/bigcommerce/customer/card/use-add-item.tsx new file mode 100644 index 000000000..7e3afa9c5 --- /dev/null +++ b/framework/bigcommerce/customer/card/use-add-item.tsx @@ -0,0 +1,15 @@ +import useAddItem, { UseAddItem } from '@commerce/customer/card/use-add-item' +import { MutationHook } from '@commerce/utils/types' + +export default useAddItem as UseAddItem + +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ fetch }) => + () => + async () => ({}), +} diff --git a/framework/bigcommerce/lib/normalize.ts b/framework/bigcommerce/lib/normalize.ts index 82a22de00..fee1fbcc3 100644 --- a/framework/bigcommerce/lib/normalize.ts +++ b/framework/bigcommerce/lib/normalize.ts @@ -91,7 +91,10 @@ export function normalizeCart(data: BigcommerceCart): Cart { createdAt: data.created_time, currency: data.currency, taxesIncluded: data.tax_included, - lineItems: data.line_items.physical_items.map(normalizeLineItem), + lineItems: [ + ...data.line_items.physical_items.map(normalizeLineItem), + ...data.line_items.digital_items.map(normalizeLineItem), + ], lineItemsSubtotalPrice: data.base_amount, subtotalPrice: data.base_amount + data.discount_amount, totalPrice: data.cart_amount, diff --git a/framework/commerce/api/endpoints/checkout.ts b/framework/commerce/api/endpoints/checkout.ts index b39239a6a..b00bce284 100644 --- a/framework/commerce/api/endpoints/checkout.ts +++ b/framework/commerce/api/endpoints/checkout.ts @@ -1,25 +1,39 @@ import type { CheckoutSchema } from '../../types/checkout' +import type { GetAPISchema } from '..' + import { CommerceAPIError } from '../utils/errors' import isAllowedOperation from '../utils/is-allowed-operation' -import type { GetAPISchema } from '..' const checkoutEndpoint: GetAPISchema< any, CheckoutSchema >['endpoint']['handler'] = async (ctx) => { - const { req, res, handlers } = ctx + const { req, res, handlers, config } = ctx if ( !isAllowedOperation(req, res, { - GET: handlers['checkout'], + GET: handlers['getCheckout'], + POST: handlers['submitCheckout'], }) ) { return } + const { cookies } = req + const cartId = cookies[config.cartCookie] + try { - const body = null - return await handlers['checkout']({ ...ctx, body }) + // Create checkout + if (req.method === 'GET') { + const body = { ...req.body, cartId } + return await handlers['getCheckout']?.({ ...ctx, body }) + } + + // Create checkout + if (req.method === 'POST') { + const body = { ...req.body, cartId } + return await handlers['submitCheckout']({ ...ctx, body }) + } } catch (error) { console.error(error) diff --git a/framework/commerce/api/endpoints/customer/address.ts b/framework/commerce/api/endpoints/customer/address.ts new file mode 100644 index 000000000..d5ede697a --- /dev/null +++ b/framework/commerce/api/endpoints/customer/address.ts @@ -0,0 +1,65 @@ +import type { CustomerAddressSchema } from '../../../types/customer/address' +import type { GetAPISchema } from '../..' + +import { CommerceAPIError } from '../../utils/errors' +import isAllowedOperation from '../../utils/is-allowed-operation' + +const customerShippingEndpoint: GetAPISchema< + any, + CustomerAddressSchema +>['endpoint']['handler'] = async (ctx) => { + const { req, res, handlers, config } = ctx + + if ( + !isAllowedOperation(req, res, { + GET: handlers['getAddresses'], + POST: handlers['addItem'], + PUT: handlers['updateItem'], + DELETE: handlers['removeItem'], + }) + ) { + return + } + + const { cookies } = req + + // Cart id might be usefull for anonymous shopping + const cartId = cookies[config.cartCookie] + + try { + // Return customer addresses + if (req.method === 'GET') { + const body = { cartId } + return await handlers['getAddresses']({ ...ctx, body }) + } + + // Create or add an item to customer addresses list + if (req.method === 'POST') { + const body = { ...req.body, cartId } + return await handlers['addItem']({ ...ctx, body }) + } + + // Update item in customer addresses list + if (req.method === 'PUT') { + const body = { ...req.body, cartId } + return await handlers['updateItem']({ ...ctx, body }) + } + + // Remove an item from customer addresses list + if (req.method === 'DELETE') { + const body = { ...req.body, cartId } + return await handlers['removeItem']({ ...ctx, body }) + } + } catch (error) { + console.error(error) + + const message = + error instanceof CommerceAPIError + ? 'An unexpected error ocurred with the Commerce API' + : 'An unexpected error ocurred' + + res.status(500).json({ data: null, errors: [{ message }] }) + } +} + +export default customerShippingEndpoint diff --git a/framework/commerce/api/endpoints/customer/card.ts b/framework/commerce/api/endpoints/customer/card.ts new file mode 100644 index 000000000..ad268cbb9 --- /dev/null +++ b/framework/commerce/api/endpoints/customer/card.ts @@ -0,0 +1,65 @@ +import type { CustomerCardSchema } from '../../../types/customer/card' +import type { GetAPISchema } from '../..' + +import { CommerceAPIError } from '../../utils/errors' +import isAllowedOperation from '../../utils/is-allowed-operation' + +const customerCardEndpoint: GetAPISchema< + any, + CustomerCardSchema +>['endpoint']['handler'] = async (ctx) => { + const { req, res, handlers, config } = ctx + + if ( + !isAllowedOperation(req, res, { + GET: handlers['getCards'], + POST: handlers['addItem'], + PUT: handlers['updateItem'], + DELETE: handlers['removeItem'], + }) + ) { + return + } + + const { cookies } = req + + // Cart id might be usefull for anonymous shopping + const cartId = cookies[config.cartCookie] + + try { + // Create or add a card + if (req.method === 'GET') { + const body = { ...req.body } + return await handlers['getCards']({ ...ctx, body }) + } + + // Create or add an item to customer cards + if (req.method === 'POST') { + const body = { ...req.body, cartId } + return await handlers['addItem']({ ...ctx, body }) + } + + // Update item in customer cards + if (req.method === 'PUT') { + const body = { ...req.body, cartId } + return await handlers['updateItem']({ ...ctx, body }) + } + + // Remove an item from customer cards + if (req.method === 'DELETE') { + const body = { ...req.body, cartId } + return await handlers['removeItem']({ ...ctx, body }) + } + } catch (error) { + console.error(error) + + const message = + error instanceof CommerceAPIError + ? 'An unexpected error ocurred with the Commerce API' + : 'An unexpected error ocurred' + + res.status(500).json({ data: null, errors: [{ message }] }) + } +} + +export default customerCardEndpoint diff --git a/framework/commerce/api/endpoints/customer.ts b/framework/commerce/api/endpoints/customer/index.ts similarity index 75% rename from framework/commerce/api/endpoints/customer.ts rename to framework/commerce/api/endpoints/customer/index.ts index 6372c494f..eb2a048b7 100644 --- a/framework/commerce/api/endpoints/customer.ts +++ b/framework/commerce/api/endpoints/customer/index.ts @@ -1,7 +1,8 @@ -import type { CustomerSchema } from '../../types/customer' -import { CommerceAPIError } from '../utils/errors' -import isAllowedOperation from '../utils/is-allowed-operation' -import type { GetAPISchema } from '..' +import type { CustomerSchema } from '../../../types/customer' +import type { GetAPISchema } from '../..' + +import { CommerceAPIError } from '../../utils/errors' +import isAllowedOperation from '../../utils/is-allowed-operation' const customerEndpoint: GetAPISchema< any, diff --git a/framework/commerce/api/index.ts b/framework/commerce/api/index.ts index 32fe8cf80..716c11ed5 100644 --- a/framework/commerce/api/index.ts +++ b/framework/commerce/api/index.ts @@ -9,6 +9,8 @@ import type { SignupSchema } from '../types/signup' import type { ProductsSchema } from '../types/product' import type { WishlistSchema } from '../types/wishlist' import type { CheckoutSchema } from '../types/checkout' +import type { CustomerCardSchema } from '../types/customer/card' +import type { CustomerAddressSchema } from '../types/customer/address' import { defaultOperations, OPERATIONS, @@ -25,6 +27,8 @@ export type APISchemas = | ProductsSchema | WishlistSchema | CheckoutSchema + | CustomerCardSchema + | CustomerAddressSchema export type GetAPISchema< C extends CommerceAPI, diff --git a/framework/commerce/checkout/use-checkout.ts b/framework/commerce/checkout/use-checkout.ts new file mode 100644 index 000000000..0fe74cb28 --- /dev/null +++ b/framework/commerce/checkout/use-checkout.ts @@ -0,0 +1,34 @@ +import type { SWRHook, HookFetcherFn } from '../utils/types' +import type { GetCheckoutHook } from '../types/checkout' + +import Cookies from 'js-cookie' + +import { useHook, useSWRHook } from '../utils/use-hook' +import { Provider, useCommerce } from '..' + +export type UseCheckout< + H extends SWRHook> = SWRHook +> = ReturnType + +export const fetcher: HookFetcherFn = async ({ + options, + input: { cartId }, + fetch, +}) => { + return cartId ? await fetch(options) : null +} + +const fn = (provider: Provider) => provider.checkout?.useCheckout! + +const useCheckout: UseCheckout = (input) => { + const hook = useHook(fn) + const { cartCookie } = useCommerce() + const fetcherFn = hook.fetcher ?? fetcher + const wrapper: typeof fetcher = (context) => { + context.input.cartId = Cookies.get(cartCookie) + return fetcherFn(context) + } + return useSWRHook({ ...hook, fetcher: wrapper })(input) +} + +export default useCheckout diff --git a/framework/commerce/checkout/use-submit-checkout.tsx b/framework/commerce/checkout/use-submit-checkout.tsx new file mode 100644 index 000000000..a5d865002 --- /dev/null +++ b/framework/commerce/checkout/use-submit-checkout.tsx @@ -0,0 +1,23 @@ +import type { HookFetcherFn, MutationHook } from '../utils/types' +import type { SubmitCheckoutHook } from '../types/checkout' +import type { Provider } from '..' + +import { useHook, useMutationHook } from '../utils/use-hook' +import { mutationFetcher } from '../utils/default-fetcher' + +export type UseSubmitCheckout< + H extends MutationHook< + SubmitCheckoutHook + > = MutationHook +> = ReturnType + +export const fetcher: HookFetcherFn = mutationFetcher + +const fn = (provider: Provider) => provider.checkout?.useSubmitCheckout! + +const useSubmitCheckout: UseSubmitCheckout = (...args) => { + const hook = useHook(fn) + return useMutationHook({ fetcher, ...hook })(...args) +} + +export default useSubmitCheckout diff --git a/framework/commerce/customer/address/use-add-item.tsx b/framework/commerce/customer/address/use-add-item.tsx new file mode 100644 index 000000000..94c45142e --- /dev/null +++ b/framework/commerce/customer/address/use-add-item.tsx @@ -0,0 +1,21 @@ +import type { HookFetcherFn, MutationHook } from '../../utils/types' +import type { AddItemHook } from '../../types/customer/address' +import type { Provider } from '../..' + +import { useHook, useMutationHook } from '../../utils/use-hook' +import { mutationFetcher } from '../../utils/default-fetcher' + +export type UseAddItem< + H extends MutationHook> = MutationHook +> = ReturnType + +export const fetcher: HookFetcherFn = mutationFetcher + +const fn = (provider: Provider) => provider.customer?.address?.useAddItem! + +const useAddItem: UseAddItem = (...args) => { + const hook = useHook(fn) + return useMutationHook({ fetcher, ...hook })(...args) +} + +export default useAddItem diff --git a/framework/commerce/customer/address/use-addresses.tsx b/framework/commerce/customer/address/use-addresses.tsx new file mode 100644 index 000000000..7fc12924c --- /dev/null +++ b/framework/commerce/customer/address/use-addresses.tsx @@ -0,0 +1,34 @@ +import type { SWRHook, HookFetcherFn } from '../../utils/types' +import type { GetAddressesHook } from '../../types/customer/address' + +import Cookies from 'js-cookie' + +import { useHook, useSWRHook } from '../../utils/use-hook' +import { Provider, useCommerce } from '../..' + +export type UseAddresses< + H extends SWRHook> = SWRHook +> = ReturnType + +export const fetcher: HookFetcherFn = async ({ + options, + input: { cartId }, + fetch, +}) => { + return cartId ? await fetch(options) : null +} + +const fn = (provider: Provider) => provider.customer?.address?.useAddresses! + +const useAddresses: UseAddresses = (input) => { + const hook = useHook(fn) + const { cartCookie } = useCommerce() + const fetcherFn = hook.fetcher ?? fetcher + const wrapper: typeof fetcher = (context) => { + context.input.cartId = Cookies.get(cartCookie) + return fetcherFn(context) + } + return useSWRHook({ ...hook, fetcher: wrapper })(input) +} + +export default useAddresses diff --git a/framework/commerce/customer/address/use-remove-item.tsx b/framework/commerce/customer/address/use-remove-item.tsx new file mode 100644 index 000000000..820a65dad --- /dev/null +++ b/framework/commerce/customer/address/use-remove-item.tsx @@ -0,0 +1,21 @@ +import type { HookFetcherFn, MutationHook } from '../../utils/types' +import type { RemoveItemHook } from '../../types/customer/address' +import type { Provider } from '../..' + +import { useHook, useMutationHook } from '../../utils/use-hook' +import { mutationFetcher } from '../../utils/default-fetcher' + +export type UseRemoveItem< + H extends MutationHook> = MutationHook +> = ReturnType + +export const fetcher: HookFetcherFn = mutationFetcher + +const fn = (provider: Provider) => provider.customer?.address?.useRemoveItem! + +const useRemoveItem: UseRemoveItem = (input) => { + const hook = useHook(fn) + return useMutationHook({ fetcher, ...hook })(input) +} + +export default useRemoveItem diff --git a/framework/commerce/customer/address/use-update-item.tsx b/framework/commerce/customer/address/use-update-item.tsx new file mode 100644 index 000000000..d05882296 --- /dev/null +++ b/framework/commerce/customer/address/use-update-item.tsx @@ -0,0 +1,21 @@ +import type { HookFetcherFn, MutationHook } from '../../utils/types' +import type { UpdateItemHook } from '../../types/customer/address' +import type { Provider } from '../..' + +import { useHook, useMutationHook } from '../../utils/use-hook' +import { mutationFetcher } from '../../utils/default-fetcher' + +export type UseUpdateItem< + H extends MutationHook> = MutationHook +> = ReturnType + +export const fetcher: HookFetcherFn = mutationFetcher + +const fn = (provider: Provider) => provider.customer?.address?.useUpdateItem! + +const useUpdateItem: UseUpdateItem = (input) => { + const hook = useHook(fn) + return useMutationHook({ fetcher, ...hook })(input) +} + +export default useUpdateItem diff --git a/framework/commerce/customer/card/use-add-item.tsx b/framework/commerce/customer/card/use-add-item.tsx new file mode 100644 index 000000000..7b4ffdb17 --- /dev/null +++ b/framework/commerce/customer/card/use-add-item.tsx @@ -0,0 +1,21 @@ +import type { HookFetcherFn, MutationHook } from '../../utils/types' +import type { AddItemHook } from '../../types/customer/card' +import type { Provider } from '../..' + +import { useHook, useMutationHook } from '../../utils/use-hook' +import { mutationFetcher } from '../../utils/default-fetcher' + +export type UseAddItem< + H extends MutationHook> = MutationHook +> = ReturnType + +export const fetcher: HookFetcherFn = mutationFetcher + +const fn = (provider: Provider) => provider.customer?.card?.useAddItem! + +const useAddItem: UseAddItem = (...args) => { + const hook = useHook(fn) + return useMutationHook({ fetcher, ...hook })(...args) +} + +export default useAddItem diff --git a/framework/commerce/customer/card/use-cards.tsx b/framework/commerce/customer/card/use-cards.tsx new file mode 100644 index 000000000..57099504f --- /dev/null +++ b/framework/commerce/customer/card/use-cards.tsx @@ -0,0 +1,34 @@ +import type { SWRHook, HookFetcherFn } from '../../utils/types' +import type { GetCardsHook } from '../../types/customer/card' + +import Cookies from 'js-cookie' + +import { useHook, useSWRHook } from '../../utils/use-hook' +import { Provider, useCommerce } from '../..' + +export type UseCards< + H extends SWRHook> = SWRHook +> = ReturnType + +export const fetcher: HookFetcherFn = async ({ + options, + input: { cartId }, + fetch, +}) => { + return cartId ? await fetch(options) : null +} + +const fn = (provider: Provider) => provider.customer?.card?.useCards! + +const useCards: UseCards = (input) => { + const hook = useHook(fn) + const { cartCookie } = useCommerce() + const fetcherFn = hook.fetcher ?? fetcher + const wrapper: typeof fetcher = (context) => { + context.input.cartId = Cookies.get(cartCookie) + return fetcherFn(context) + } + return useSWRHook({ ...hook, fetcher: wrapper })(input) +} + +export default useCards diff --git a/framework/commerce/customer/card/use-remove-item.tsx b/framework/commerce/customer/card/use-remove-item.tsx new file mode 100644 index 000000000..1d85fa636 --- /dev/null +++ b/framework/commerce/customer/card/use-remove-item.tsx @@ -0,0 +1,21 @@ +import type { HookFetcherFn, MutationHook } from '../../utils/types' +import type { RemoveItemHook } from '../../types/customer/card' +import type { Provider } from '../..' + +import { useHook, useMutationHook } from '../../utils/use-hook' +import { mutationFetcher } from '../../utils/default-fetcher' + +export type UseRemoveItem< + H extends MutationHook> = MutationHook +> = ReturnType + +export const fetcher: HookFetcherFn = mutationFetcher + +const fn = (provider: Provider) => provider.customer?.card?.useRemoveItem! + +const useRemoveItem: UseRemoveItem = (input) => { + const hook = useHook(fn) + return useMutationHook({ fetcher, ...hook })(input) +} + +export default useRemoveItem diff --git a/framework/commerce/customer/card/use-update-item.tsx b/framework/commerce/customer/card/use-update-item.tsx new file mode 100644 index 000000000..cd8837d7f --- /dev/null +++ b/framework/commerce/customer/card/use-update-item.tsx @@ -0,0 +1,21 @@ +import type { HookFetcherFn, MutationHook } from '../../utils/types' +import type { UpdateItemHook } from '../../types/customer/card' +import type { Provider } from '../..' + +import { useHook, useMutationHook } from '../../utils/use-hook' +import { mutationFetcher } from '../../utils/default-fetcher' + +export type UseUpdateItem< + H extends MutationHook> = MutationHook +> = ReturnType + +export const fetcher: HookFetcherFn = mutationFetcher + +const fn = (provider: Provider) => provider?.customer?.card?.useUpdateItem! + +const useUpdateItem: UseUpdateItem = (input) => { + const hook = useHook(fn) + return useMutationHook({ fetcher, ...hook })(input) +} + +export default useUpdateItem diff --git a/framework/commerce/index.tsx b/framework/commerce/index.tsx index dd740809f..eaa878a9e 100644 --- a/framework/commerce/index.tsx +++ b/framework/commerce/index.tsx @@ -15,6 +15,7 @@ import type { Signup, Login, Logout, + Checkout, } from '@commerce/types' import type { Fetcher, SWRHook, MutationHook } from './utils/types' @@ -29,6 +30,10 @@ export type Provider = CommerceConfig & { useUpdateItem?: MutationHook useRemoveItem?: MutationHook } + checkout?: { + useCheckout?: SWRHook + useSubmitCheckout?: MutationHook + } wishlist?: { useWishlist?: SWRHook useAddItem?: MutationHook @@ -36,6 +41,18 @@ export type Provider = CommerceConfig & { } customer?: { useCustomer?: SWRHook + card?: { + useCards?: SWRHook + useAddItem?: MutationHook + useUpdateItem?: MutationHook + useRemoveItem?: MutationHook + } + address?: { + useAddresses?: SWRHook + useAddItem?: MutationHook + useUpdateItem?: MutationHook + useRemoveItem?: MutationHook + } } products?: { useSearch?: SWRHook diff --git a/framework/commerce/types/checkout.ts b/framework/commerce/types/checkout.ts index 9e3c7ecfa..ea28926a8 100644 --- a/framework/commerce/types/checkout.ts +++ b/framework/commerce/types/checkout.ts @@ -1,10 +1,57 @@ -export type CheckoutSchema = { +import type { UseSubmitCheckout } from '../checkout/use-submit-checkout' +import type { Address } from './customer/address' +import type { Card } from './customer/card' + +// Index +export type Checkout = unknown; + +export type CheckoutTypes = { + card?: Card + address?: Address + checkout?: Checkout + hasPayment?: boolean + hasShipping?: boolean +} + +export type SubmitCheckoutHook = { + data: T + input?: T + fetcherInput: T + body: { item: T } + actionInput: T +} + +export type GetCheckoutHook = { + data: T['checkout'] | null + input: {} + fetcherInput: { cartId?: string } + swrState: { isEmpty: boolean } + mutations: { submit: UseSubmitCheckout } +} + +export type CheckoutHooks = { + submitCheckout: SubmitCheckoutHook + getCheckout: GetCheckoutHook +} + +export type GetCheckoutHandler = + GetCheckoutHook & { + body: { cartId: string } + } + +export type SubmitCheckoutHandler = + SubmitCheckoutHook & { + body: { cartId: string } + } + +export type CheckoutHandlers = { + getCheckout?: GetCheckoutHandler + submitCheckout: SubmitCheckoutHandler +} + +export type CheckoutSchema = { endpoint: { options: {} - handlers: { - checkout: { - data: null - } - } + handlers: CheckoutHandlers } } diff --git a/framework/commerce/types/customer/address.ts b/framework/commerce/types/customer/address.ts new file mode 100644 index 000000000..5b6ca4b49 --- /dev/null +++ b/framework/commerce/types/customer/address.ts @@ -0,0 +1,93 @@ +export interface Address { + id: string; + mask: string; +} + +export interface AddressFields { + type: string; + firstName: string; + lastName: string; + company: string; + streetNumber: string; + apartments: string; + zipCode: string; + city: string; + country: string; +} + +export type CustomerAddressTypes = { + address?: Address; + fields: AddressFields; +} + +export type GetAddressesHook = { + data: T['address'] | null + input: {} + fetcherInput: { cartId?: string } + swrState: { isEmpty: boolean } +} + +export type AddItemHook = { + data: T['address'] + input?: T['fields'] + fetcherInput: T['fields'] + body: { item: T['fields'] } + actionInput: T['fields'] +} + +export type UpdateItemHook = { + data: T['address'] | null + input: { item?: T['fields']; wait?: number } + fetcherInput: { itemId: string; item: T['fields'] } + body: { itemId: string; item: T['fields'] } + actionInput: T['fields'] & { id: string } +} + +export type RemoveItemHook = { + data: T['address'] | null + input: { item?: T['fields'] } + fetcherInput: { itemId: string } + body: { itemId: string } + actionInput: { id: string } +} + +export type CustomerAddressHooks = { + getAddresses: GetAddressesHook + addItem: AddItemHook + updateItem: UpdateItemHook + removeItem: RemoveItemHook +} + +export type AddresssHandler = GetAddressesHook & { + body: { cartId?: string } +} + +export type AddItemHandler = AddItemHook & { + body: { cartId: string } +} + +export type UpdateItemHandler = + UpdateItemHook & { + data: T['address'] + body: { cartId: string } + } + +export type RemoveItemHandler = + RemoveItemHook & { + body: { cartId: string } + } + + +export type CustomerAddressHandlers = { + getAddresses: GetAddressesHook + addItem: AddItemHandler + updateItem: UpdateItemHandler + removeItem: RemoveItemHandler +} + +export type CustomerAddressSchema = { + endpoint: { + options: {} + handlers: CustomerAddressHandlers + } +} diff --git a/framework/commerce/types/customer/card.ts b/framework/commerce/types/customer/card.ts new file mode 100644 index 000000000..a8731411f --- /dev/null +++ b/framework/commerce/types/customer/card.ts @@ -0,0 +1,96 @@ +export interface Card { + id: string; + mask: string; + provider: string; +} + +export interface CardFields { + cardHolder: string; + cardNumber: string; + cardExpireDate: string; + cardCvc: string; + firstName: string; + lastName: string; + company: string; + streetNumber: string; + zipCode: string; + city: string; + country: string; +} + +export type CustomerCardTypes = { + card?: Card; + fields: CardFields; +} + +export type GetCardsHook = { + data: T['card'] | null + input: {} + fetcherInput: { cartId?: string } + swrState: { isEmpty: boolean } +} + +export type AddItemHook = { + data: T['card'] + input?: T['fields'] + fetcherInput: T['fields'] + body: { item: T['fields'] } + actionInput: T['fields'] +} + +export type UpdateItemHook = { + data: T['card'] | null + input: { item?: T['fields']; wait?: number } + fetcherInput: { itemId: string; item: T['fields'] } + body: { itemId: string; item: T['fields'] } + actionInput: T['fields'] & { id: string } +} + +export type RemoveItemHook = { + data: T['card'] | null + input: { item?: T['fields'] } + fetcherInput: { itemId: string } + body: { itemId: string } + actionInput: { id: string } +} + +export type CustomerCardHooks = { + getCards: GetCardsHook + addItem: AddItemHook + updateItem: UpdateItemHook + removeItem: RemoveItemHook +} + +export type CardsHandler = GetCardsHook & { + body: { cartId?: string } +} + +export type AddItemHandler = AddItemHook & { + body: { cartId: string } +} + +export type UpdateItemHandler = + UpdateItemHook & { + data: T['card'] + body: { cartId: string } + } + +export type RemoveItemHandler = + RemoveItemHook & { + body: { cartId: string } + } + + +export type CustomerCardHandlers = { + getCards: GetCardsHook + addItem: AddItemHandler + updateItem: UpdateItemHandler + removeItem: RemoveItemHandler +} + +export type CustomerCardSchema = { + endpoint: { + options: {} + handlers: CustomerCardHandlers + } +} diff --git a/framework/commerce/types/customer.ts b/framework/commerce/types/customer/index.ts similarity index 87% rename from framework/commerce/types/customer.ts rename to framework/commerce/types/customer/index.ts index ba90acdf4..70c437c29 100644 --- a/framework/commerce/types/customer.ts +++ b/framework/commerce/types/customer/index.ts @@ -1,3 +1,6 @@ +export * as Card from "./card" +export * as Address from "./address" + // TODO: define this type export type Customer = any diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index 751cea4a5..2bca30852 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -87,6 +87,8 @@ export type HookSchemaBase = { export type SWRHookSchemaBase = HookSchemaBase & { // Custom state added to the response object of SWR swrState?: {} + // Instances of MutationSchemaBase that the hook returns for better DX + mutations?: Record['useHook']>> } export type MutationSchemaBase = HookSchemaBase & { @@ -102,7 +104,7 @@ export type SWRHook = { context: SWRHookContext ): HookFunction< H['input'] & { swrOptions?: SwrOptions }, - ResponseState & H['swrState'] + ResponseState & H['swrState'] & H['mutations'] > fetchOptions: HookFetcherOptions fetcher?: HookFetcherFn diff --git a/framework/local/api/endpoints/customer/address.ts b/framework/local/api/endpoints/customer/address.ts new file mode 100644 index 000000000..491bf0ac9 --- /dev/null +++ b/framework/local/api/endpoints/customer/address.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/local/api/endpoints/customer/card.ts b/framework/local/api/endpoints/customer/card.ts new file mode 100644 index 000000000..491bf0ac9 --- /dev/null +++ b/framework/local/api/endpoints/customer/card.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/local/checkout/use-checkout.tsx b/framework/local/checkout/use-checkout.tsx new file mode 100644 index 000000000..942f85b83 --- /dev/null +++ b/framework/local/checkout/use-checkout.tsx @@ -0,0 +1,14 @@ +import { SWRHook } from '@commerce/utils/types' +import useCheckout, { UseCheckout } from '@commerce/checkout/use-checkout' + +export default useCheckout as UseCheckout + +export const handler: SWRHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ useData }) => + async (input) => ({}), +} diff --git a/framework/local/customer/address/use-add-item.tsx b/framework/local/customer/address/use-add-item.tsx new file mode 100644 index 000000000..ac9dcd5cf --- /dev/null +++ b/framework/local/customer/address/use-add-item.tsx @@ -0,0 +1,15 @@ +import useAddItem, { UseAddItem } from '@commerce/customer/address/use-add-item' +import { MutationHook } from '@commerce/utils/types' + +export default useAddItem as UseAddItem + +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ fetch }) => + () => + async () => ({}), +} diff --git a/framework/local/customer/card/use-add-item.tsx b/framework/local/customer/card/use-add-item.tsx new file mode 100644 index 000000000..7e3afa9c5 --- /dev/null +++ b/framework/local/customer/card/use-add-item.tsx @@ -0,0 +1,15 @@ +import useAddItem, { UseAddItem } from '@commerce/customer/card/use-add-item' +import { MutationHook } from '@commerce/utils/types' + +export default useAddItem as UseAddItem + +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ fetch }) => + () => + async () => ({}), +} diff --git a/framework/saleor/api/endpoints/checkout/index.ts b/framework/saleor/api/endpoints/checkout/index.ts index f15672435..385283ea7 100644 --- a/framework/saleor/api/endpoints/checkout/index.ts +++ b/framework/saleor/api/endpoints/checkout/index.ts @@ -6,11 +6,7 @@ export type CheckoutAPI = GetAPISchema export type CheckoutEndpoint = CheckoutAPI['endpoint'] -const checkout: CheckoutEndpoint['handlers']['checkout'] = async ({ - req, - res, - config, -}) => { +const submitCheckout: CheckoutEndpoint['handlers']['submitCheckout'] = async ({ req, res, config }) => { try { const html = ` @@ -47,7 +43,7 @@ const checkout: CheckoutEndpoint['handlers']['checkout'] = async ({ } } -export const handlers: CheckoutEndpoint['handlers'] = { checkout } +export const handlers: CheckoutEndpoint['handlers'] = { submitCheckout } const checkoutApi = createEndpoint({ handler: checkoutEndpoint, diff --git a/framework/saleor/api/endpoints/customer.ts b/framework/saleor/api/endpoints/customer.ts deleted file mode 100644 index d09c976c3..000000000 --- a/framework/saleor/api/endpoints/customer.ts +++ /dev/null @@ -1 +0,0 @@ -export default function (_commerce: any) {} diff --git a/framework/saleor/api/endpoints/customer/address.ts b/framework/saleor/api/endpoints/customer/address.ts new file mode 100644 index 000000000..491bf0ac9 --- /dev/null +++ b/framework/saleor/api/endpoints/customer/address.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/saleor/api/endpoints/customer/card.ts b/framework/saleor/api/endpoints/customer/card.ts new file mode 100644 index 000000000..491bf0ac9 --- /dev/null +++ b/framework/saleor/api/endpoints/customer/card.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/saleor/api/endpoints/customer/index.ts b/framework/saleor/api/endpoints/customer/index.ts new file mode 100644 index 000000000..491bf0ac9 --- /dev/null +++ b/framework/saleor/api/endpoints/customer/index.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/saleor/checkout/use-checkout.tsx b/framework/saleor/checkout/use-checkout.tsx new file mode 100644 index 000000000..942f85b83 --- /dev/null +++ b/framework/saleor/checkout/use-checkout.tsx @@ -0,0 +1,14 @@ +import { SWRHook } from '@commerce/utils/types' +import useCheckout, { UseCheckout } from '@commerce/checkout/use-checkout' + +export default useCheckout as UseCheckout + +export const handler: SWRHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ useData }) => + async (input) => ({}), +} diff --git a/framework/saleor/customer/address/use-add-item.tsx b/framework/saleor/customer/address/use-add-item.tsx new file mode 100644 index 000000000..ac9dcd5cf --- /dev/null +++ b/framework/saleor/customer/address/use-add-item.tsx @@ -0,0 +1,15 @@ +import useAddItem, { UseAddItem } from '@commerce/customer/address/use-add-item' +import { MutationHook } from '@commerce/utils/types' + +export default useAddItem as UseAddItem + +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ fetch }) => + () => + async () => ({}), +} diff --git a/framework/saleor/customer/card/use-add-item.tsx b/framework/saleor/customer/card/use-add-item.tsx new file mode 100644 index 000000000..7e3afa9c5 --- /dev/null +++ b/framework/saleor/customer/card/use-add-item.tsx @@ -0,0 +1,15 @@ +import useAddItem, { UseAddItem } from '@commerce/customer/card/use-add-item' +import { MutationHook } from '@commerce/utils/types' + +export default useAddItem as UseAddItem + +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ fetch }) => + () => + async () => ({}), +} diff --git a/framework/shopify/README.md b/framework/shopify/README.md index d5c4aa942..82d90fc55 100644 --- a/framework/shopify/README.md +++ b/framework/shopify/README.md @@ -2,7 +2,7 @@ **Demo:** https://shopify.demo.vercel.store/ -Before getting starter, a [Shopify](https://www.shopify.com/) account and store is required before using the provider. +Before getting started, a [Shopify](https://www.shopify.com/) account and store is required before using the provider. Next, copy the `.env.template` file in this directory to `.env.local` in the main directory (which will be ignored by Git): diff --git a/framework/shopify/api/endpoints/checkout/index.ts b/framework/shopify/api/endpoints/checkout/index.ts index 5d78f451b..72b86cae2 100644 --- a/framework/shopify/api/endpoints/checkout/index.ts +++ b/framework/shopify/api/endpoints/checkout/index.ts @@ -2,13 +2,13 @@ import { GetAPISchema, createEndpoint } from '@commerce/api' import checkoutEndpoint from '@commerce/api/endpoints/checkout' import type { CheckoutSchema } from '../../../types/checkout' import type { ShopifyAPI } from '../..' -import checkout from './checkout' +import submitCheckout from './submit-checkout' export type CheckoutAPI = GetAPISchema export type CheckoutEndpoint = CheckoutAPI['endpoint'] -export const handlers: CheckoutEndpoint['handlers'] = { checkout } +export const handlers: CheckoutEndpoint['handlers'] = { submitCheckout } const checkoutApi = createEndpoint({ handler: checkoutEndpoint, diff --git a/framework/shopify/api/endpoints/checkout/checkout.ts b/framework/shopify/api/endpoints/checkout/submit-checkout.ts similarity index 88% rename from framework/shopify/api/endpoints/checkout/checkout.ts rename to framework/shopify/api/endpoints/checkout/submit-checkout.ts index 0c340a129..97ac77381 100644 --- a/framework/shopify/api/endpoints/checkout/checkout.ts +++ b/framework/shopify/api/endpoints/checkout/submit-checkout.ts @@ -6,7 +6,7 @@ import { import associateCustomerWithCheckoutMutation from '../../../utils/mutations/associate-customer-with-checkout' import type { CheckoutEndpoint } from '.' -const checkout: CheckoutEndpoint['handlers']['checkout'] = async ({ +const submitCheckout: CheckoutEndpoint['handlers']['submitCheckout'] = async ({ req, res, config, @@ -35,4 +35,4 @@ const checkout: CheckoutEndpoint['handlers']['checkout'] = async ({ } } -export default checkout +export default submitCheckout diff --git a/framework/shopify/api/endpoints/customer.ts b/framework/shopify/api/endpoints/customer.ts deleted file mode 100644 index d09c976c3..000000000 --- a/framework/shopify/api/endpoints/customer.ts +++ /dev/null @@ -1 +0,0 @@ -export default function (_commerce: any) {} diff --git a/framework/shopify/api/endpoints/customer/address.ts b/framework/shopify/api/endpoints/customer/address.ts new file mode 100644 index 000000000..491bf0ac9 --- /dev/null +++ b/framework/shopify/api/endpoints/customer/address.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/shopify/api/endpoints/customer/card.ts b/framework/shopify/api/endpoints/customer/card.ts new file mode 100644 index 000000000..491bf0ac9 --- /dev/null +++ b/framework/shopify/api/endpoints/customer/card.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/shopify/api/endpoints/customer/index.ts b/framework/shopify/api/endpoints/customer/index.ts new file mode 100644 index 000000000..491bf0ac9 --- /dev/null +++ b/framework/shopify/api/endpoints/customer/index.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/shopify/checkout/use-checkout.tsx b/framework/shopify/checkout/use-checkout.tsx new file mode 100644 index 000000000..942f85b83 --- /dev/null +++ b/framework/shopify/checkout/use-checkout.tsx @@ -0,0 +1,14 @@ +import { SWRHook } from '@commerce/utils/types' +import useCheckout, { UseCheckout } from '@commerce/checkout/use-checkout' + +export default useCheckout as UseCheckout + +export const handler: SWRHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ useData }) => + async (input) => ({}), +} diff --git a/framework/shopify/const.ts b/framework/shopify/const.ts index 06fbe5054..a8ee70586 100644 --- a/framework/shopify/const.ts +++ b/framework/shopify/const.ts @@ -8,6 +8,6 @@ export const STORE_DOMAIN = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN export const SHOPIFY_COOKIE_EXPIRE = 30 -export const API_URL = `https://${STORE_DOMAIN}/api/2021-01/graphql.json` +export const API_URL = `https://${STORE_DOMAIN}/api/2021-07/graphql.json` export const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN diff --git a/framework/shopify/customer/address/use-add-item.tsx b/framework/shopify/customer/address/use-add-item.tsx new file mode 100644 index 000000000..ac9dcd5cf --- /dev/null +++ b/framework/shopify/customer/address/use-add-item.tsx @@ -0,0 +1,15 @@ +import useAddItem, { UseAddItem } from '@commerce/customer/address/use-add-item' +import { MutationHook } from '@commerce/utils/types' + +export default useAddItem as UseAddItem + +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ fetch }) => + () => + async () => ({}), +} diff --git a/framework/shopify/customer/card/use-add-item.tsx b/framework/shopify/customer/card/use-add-item.tsx new file mode 100644 index 000000000..7e3afa9c5 --- /dev/null +++ b/framework/shopify/customer/card/use-add-item.tsx @@ -0,0 +1,15 @@ +import useAddItem, { UseAddItem } from '@commerce/customer/card/use-add-item' +import { MutationHook } from '@commerce/utils/types' + +export default useAddItem as UseAddItem + +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ fetch }) => + () => + async () => ({}), +} diff --git a/framework/swell/api/endpoints/checkout/index.ts b/framework/swell/api/endpoints/checkout/index.ts index ab17a3767..9847d8420 100644 --- a/framework/swell/api/endpoints/checkout/index.ts +++ b/framework/swell/api/endpoints/checkout/index.ts @@ -3,7 +3,7 @@ import { CheckoutSchema } from '@commerce/types/checkout' import { SWELL_CHECKOUT_URL_COOKIE } from '../../../const' import checkoutEndpoint from '@commerce/api/endpoints/checkout' -const checkout: CheckoutEndpoint['handlers']['checkout'] = async ({ +const submitCheckout: CheckoutEndpoint['handlers']['submitCheckout'] = async ({ req, res, config, @@ -17,7 +17,7 @@ const checkout: CheckoutEndpoint['handlers']['checkout'] = async ({ res.redirect('/cart') } } -export const handlers: CheckoutEndpoint['handlers'] = { checkout } +export const handlers: CheckoutEndpoint['handlers'] = { submitCheckout } export type CheckoutAPI = GetAPISchema export type CheckoutEndpoint = CheckoutAPI['endpoint'] diff --git a/framework/swell/api/endpoints/customer.ts b/framework/swell/api/endpoints/customer.ts deleted file mode 100644 index d09c976c3..000000000 --- a/framework/swell/api/endpoints/customer.ts +++ /dev/null @@ -1 +0,0 @@ -export default function (_commerce: any) {} diff --git a/framework/swell/api/endpoints/customer/address.ts b/framework/swell/api/endpoints/customer/address.ts new file mode 100644 index 000000000..491bf0ac9 --- /dev/null +++ b/framework/swell/api/endpoints/customer/address.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/swell/api/endpoints/customer/card.ts b/framework/swell/api/endpoints/customer/card.ts new file mode 100644 index 000000000..491bf0ac9 --- /dev/null +++ b/framework/swell/api/endpoints/customer/card.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/swell/api/endpoints/customer/index.ts b/framework/swell/api/endpoints/customer/index.ts new file mode 100644 index 000000000..491bf0ac9 --- /dev/null +++ b/framework/swell/api/endpoints/customer/index.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/swell/checkout/use-checkout.tsx b/framework/swell/checkout/use-checkout.tsx new file mode 100644 index 000000000..942f85b83 --- /dev/null +++ b/framework/swell/checkout/use-checkout.tsx @@ -0,0 +1,14 @@ +import { SWRHook } from '@commerce/utils/types' +import useCheckout, { UseCheckout } from '@commerce/checkout/use-checkout' + +export default useCheckout as UseCheckout + +export const handler: SWRHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ useData }) => + async (input) => ({}), +} diff --git a/framework/swell/customer/address/use-add-item.tsx b/framework/swell/customer/address/use-add-item.tsx new file mode 100644 index 000000000..ac9dcd5cf --- /dev/null +++ b/framework/swell/customer/address/use-add-item.tsx @@ -0,0 +1,15 @@ +import useAddItem, { UseAddItem } from '@commerce/customer/address/use-add-item' +import { MutationHook } from '@commerce/utils/types' + +export default useAddItem as UseAddItem + +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ fetch }) => + () => + async () => ({}), +} diff --git a/framework/swell/customer/card/use-add-item.tsx b/framework/swell/customer/card/use-add-item.tsx new file mode 100644 index 000000000..7e3afa9c5 --- /dev/null +++ b/framework/swell/customer/card/use-add-item.tsx @@ -0,0 +1,15 @@ +import useAddItem, { UseAddItem } from '@commerce/customer/card/use-add-item' +import { MutationHook } from '@commerce/utils/types' + +export default useAddItem as UseAddItem + +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ fetch }) => + () => + async () => ({}), +} diff --git a/framework/vendure/api/endpoints/checkout/index.ts b/framework/vendure/api/endpoints/checkout/index.ts index 00c17f993..f138af6be 100644 --- a/framework/vendure/api/endpoints/checkout/index.ts +++ b/framework/vendure/api/endpoints/checkout/index.ts @@ -3,7 +3,7 @@ import { CommerceAPI, createEndpoint, GetAPISchema } from '@commerce/api' import { CheckoutSchema } from '@commerce/types/checkout' import checkoutEndpoint from '@commerce/api/endpoints/checkout' -const checkout: CheckoutEndpoint['handlers']['checkout'] = async ({ +const submitCheckout: CheckoutEndpoint['handlers']['submitCheckout'] = async ({ req, res, config, @@ -48,7 +48,7 @@ export type CheckoutAPI = GetAPISchema export type CheckoutEndpoint = CheckoutAPI['endpoint'] -export const handlers: CheckoutEndpoint['handlers'] = { checkout } +export const handlers: CheckoutEndpoint['handlers'] = { submitCheckout } const checkoutApi = createEndpoint({ handler: checkoutEndpoint, diff --git a/framework/vendure/api/endpoints/customer/address.ts b/framework/vendure/api/endpoints/customer/address.ts new file mode 100644 index 000000000..491bf0ac9 --- /dev/null +++ b/framework/vendure/api/endpoints/customer/address.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/vendure/api/endpoints/customer/card.ts b/framework/vendure/api/endpoints/customer/card.ts new file mode 100644 index 000000000..491bf0ac9 --- /dev/null +++ b/framework/vendure/api/endpoints/customer/card.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/vendure/checkout/use-checkout.tsx b/framework/vendure/checkout/use-checkout.tsx new file mode 100644 index 000000000..942f85b83 --- /dev/null +++ b/framework/vendure/checkout/use-checkout.tsx @@ -0,0 +1,14 @@ +import { SWRHook } from '@commerce/utils/types' +import useCheckout, { UseCheckout } from '@commerce/checkout/use-checkout' + +export default useCheckout as UseCheckout + +export const handler: SWRHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ useData }) => + async (input) => ({}), +} diff --git a/framework/vendure/customer/address/use-add-item.tsx b/framework/vendure/customer/address/use-add-item.tsx new file mode 100644 index 000000000..ac9dcd5cf --- /dev/null +++ b/framework/vendure/customer/address/use-add-item.tsx @@ -0,0 +1,15 @@ +import useAddItem, { UseAddItem } from '@commerce/customer/address/use-add-item' +import { MutationHook } from '@commerce/utils/types' + +export default useAddItem as UseAddItem + +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ fetch }) => + () => + async () => ({}), +} diff --git a/framework/vendure/customer/card/use-add-item.tsx b/framework/vendure/customer/card/use-add-item.tsx new file mode 100644 index 000000000..7e3afa9c5 --- /dev/null +++ b/framework/vendure/customer/card/use-add-item.tsx @@ -0,0 +1,15 @@ +import useAddItem, { UseAddItem } from '@commerce/customer/card/use-add-item' +import { MutationHook } from '@commerce/utils/types' + +export default useAddItem as UseAddItem + +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ fetch }) => + () => + async () => ({}), +} diff --git a/framework/vendure/fetcher.ts b/framework/vendure/fetcher.ts index bf8f0dcd8..26e1eec28 100644 --- a/framework/vendure/fetcher.ts +++ b/framework/vendure/fetcher.ts @@ -41,11 +41,13 @@ export const fetcher: Fetcher = async ({ headers, credentials: 'include', }) - if (res.ok) { - const { data } = await res.json() + const { data, errors } = await res.json() + if (errors) { + throw await new FetcherError({ status: res.status, errors }) + } return data } - + throw await getError(res) } diff --git a/framework/vendure/utils/fragments/search-result-fragment.ts b/framework/vendure/utils/fragments/search-result-fragment.ts index 6155b5b47..e143ce20b 100644 --- a/framework/vendure/utils/fragments/search-result-fragment.ts +++ b/framework/vendure/utils/fragments/search-result-fragment.ts @@ -3,7 +3,6 @@ export const searchResultFragment = /* GraphQL */ ` productId productName description - description slug sku currencyCode diff --git a/pages/api/customer/address.ts b/pages/api/customer/address.ts new file mode 100644 index 000000000..5815ea462 --- /dev/null +++ b/pages/api/customer/address.ts @@ -0,0 +1,4 @@ +import customerAddressApi from '@framework/api/endpoints/customer/address' +import commerce from '@lib/api/commerce' + +export default customerAddressApi(commerce) diff --git a/pages/api/customer/card.ts b/pages/api/customer/card.ts new file mode 100644 index 000000000..6f88b8c74 --- /dev/null +++ b/pages/api/customer/card.ts @@ -0,0 +1,4 @@ +import customerCardApi from '@framework/api/endpoints/customer/card' +import commerce from '@lib/api/commerce' + +export default customerCardApi(commerce) diff --git a/pages/api/customer.ts b/pages/api/customer/index.ts similarity index 100% rename from pages/api/customer.ts rename to pages/api/customer/index.ts diff --git a/pages/wishlist.tsx b/pages/wishlist.tsx index fd28edff4..93f1c96d3 100644 --- a/pages/wishlist.tsx +++ b/pages/wishlist.tsx @@ -2,10 +2,11 @@ import type { GetStaticPropsContext } from 'next' import commerce from '@lib/api/commerce' import { Heart } from '@components/icons' import { Layout } from '@components/common' -import { Text, Container } from '@components/ui' +import { Text, Container, Skeleton } from '@components/ui' import { useCustomer } from '@framework/customer' import { WishlistCard } from '@components/wishlist' import useWishlist from '@framework/wishlist/use-wishlist' +import rangeMap from '@lib/range-map' export async function getStaticProps({ preview, @@ -43,7 +44,15 @@ export default function Wishlist() {
My Wishlist
- {isLoading || isEmpty ? ( + {isLoading ? ( +
+ {rangeMap(12, (i) => ( + +
+ + ))} +
+ ) : isEmpty ? (
@@ -56,11 +65,13 @@ export default function Wishlist() {

) : ( - data && - // @ts-ignore Shopify - Fix this types - data.items?.map((item) => ( - - )) +
+ {data && + // @ts-ignore Shopify - Fix this types + data.items?.map((item) => ( + + ))} +
)}