From e496235adc4f15d6df38930b9b9834373406d305 Mon Sep 17 00:00:00 2001 From: Timur Suleymanov Date: Mon, 3 Jun 2024 18:51:37 +0500 Subject: [PATCH] refactoring --- .components/carousel.tsx | 40 - .components/cart/actions.ts | 83 -- .components/cart/add-to-cart.tsx | 92 -- .components/cart/close-cart.tsx | 10 - .components/cart/delete-item-button.tsx | 50 - .../cart/edit-item-quantity-button.tsx | 57 -- .components/cart/index.tsx | 14 - .components/cart/modal.tsx | 191 ---- .components/cart/open-cart.tsx | 24 - .components/grid/index.tsx | 21 - .components/grid/three-items.tsx | 57 -- .components/grid/tile.tsx | 50 - .components/icons/logo.tsx | 16 - .components/label.tsx | 34 - .components/layout/footer-menu.tsx | 46 - .components/layout/footer.tsx | 69 -- .components/layout/navbar/index.tsx | 58 -- .components/layout/navbar/mobile-menu.tsx | 100 -- .components/layout/navbar/search.tsx | 57 -- .components/layout/product-grid-items.tsx | 28 - .components/layout/search/collections.tsx | 37 - .components/layout/search/filter/dropdown.tsx | 64 -- .components/layout/search/filter/index.tsx | 41 - .components/layout/search/filter/item.tsx | 67 -- .components/loading-dots.tsx | 15 - .components/logo-square.tsx | 23 - .components/opengraph-image.tsx | 40 - .components/price.tsx | 24 - .components/product/gallery.tsx | 99 -- .components/product/product-description.tsx | 36 - .components/product/variant-selector.tsx | 106 -- .components/prose.tsx | 21 - app/diseases/page.tsx | 23 + app/layout.tsx | 35 +- app/ondemand/layout.tsx | 11 + app/ondemand/page.tsx | 3 + app/ondemand/products/[id]/page.tsx | 0 app/ondemand/products/page.tsx | 17 - app/page.tsx | 14 +- app/{[page] => page}/layout.tsx | 0 app/{[page] => page}/opengraph-image.tsx | 0 app/{[page] => page}/page.tsx | 0 app/tests/[id]/page.tsx | 10 + app/tests/page.tsx | 24 + components/contact-us.tsx | 5 + .../diseases/diseases-and-conditions.tsx | 13 + components/diseases/specialities.tsx | 15 + components/home/quicklinks.tsx | 11 + components/latest-news.tsx | 5 + components/layout/base-layout.tsx | 0 components/layout/footer.tsx | 131 +-- components/layout/header.tsx | 13 + components/layout/hero/hero-1.tsx | 5 + components/layout/hero/hero-2.tsx | 5 + components/layout/navbar/index.tsx | 58 +- components/layout/topbar.tsx | 956 ++++++++++++++++++ components/ondemand/navbar/index.tsx | 93 ++ components/science-innovation.tsx | 5 + components/tests/alphabet.tsx | 3 + components/tests/tests-table.tsx | 28 + lib/spree/README-assets/screenshots.png | Bin 117099 -> 0 bytes lib/spree/README.md | 33 - lib/spree/api/endpoints/cart/index.ts | 1 - lib/spree/api/endpoints/catalog/index.ts | 1 - lib/spree/api/endpoints/catalog/products.ts | 1 - .../api/endpoints/checkout/get-checkout.ts | 44 - lib/spree/api/endpoints/checkout/index.ts | 19 - lib/spree/api/endpoints/customer/address.ts | 1 - lib/spree/api/endpoints/customer/card.ts | 1 - lib/spree/api/endpoints/customer/index.ts | 1 - lib/spree/api/endpoints/login/index.ts | 1 - lib/spree/api/endpoints/logout/index.ts | 1 - lib/spree/api/endpoints/signup/index.ts | 1 - lib/spree/api/endpoints/wishlist/index.tsx | 1 - lib/spree/api/index.ts | 48 - lib/spree/api/operations/get-all-pages.ts | 72 -- .../api/operations/get-all-product-paths.ts | 91 -- lib/spree/api/operations/get-all-products.ts | 84 -- lib/spree/api/operations/get-all-taxons.ts | 63 -- .../api/operations/get-customer-wishlist.ts | 6 - lib/spree/api/operations/get-page.ts | 73 -- lib/spree/api/operations/get-product.ts | 81 -- lib/spree/api/operations/get-products.js | 37 - lib/spree/api/operations/get-site-info.ts | 120 --- lib/spree/api/operations/index.ts | 8 - lib/spree/api/utils/create-api-fetch.ts | 74 -- lib/spree/api/utils/fetch.ts | 3 - lib/spree/auth/index.ts | 3 - lib/spree/auth/use-login.tsx | 81 -- lib/spree/auth/use-logout.tsx | 79 -- lib/spree/auth/use-signup.tsx | 94 -- lib/spree/cart/index.ts | 4 - lib/spree/cart/use-add-item.tsx | 109 -- lib/spree/cart/use-cart.tsx | 108 -- lib/spree/cart/use-remove-item.tsx | 107 -- lib/spree/cart/use-update-item.tsx | 134 --- lib/spree/checkout/use-checkout.tsx | 17 - lib/spree/commerce.config.json | 10 - lib/spree/customer/address/use-add-item.tsx | 18 - lib/spree/customer/card/use-add-item.tsx | 19 - lib/spree/customer/index.ts | 1 - lib/spree/customer/use-customer.tsx | 75 -- lib/spree/errors/AccessTokenError.ts | 1 - lib/spree/errors/MisconfigurationError.ts | 1 - .../errors/MissingConfigurationValueError.ts | 1 - .../errors/MissingLineItemVariantError.ts | 1 - lib/spree/errors/MissingOptionValueError.ts | 1 - .../errors/MissingPrimaryVariantError.ts | 1 - lib/spree/errors/MissingProductError.ts | 1 - lib/spree/errors/MissingSlugVariableError.ts | 1 - lib/spree/errors/MissingVariantError.ts | 1 - lib/spree/errors/RefreshTokenError.ts | 1 - lib/spree/errors/SpreeResponseContentError.ts | 1 - .../SpreeSdkMethodFromEndpointPathError.ts | 1 - lib/spree/errors/TokensNotRejectedError.ts | 1 - .../errors/UserTokenResponseParseError.ts | 1 - lib/spree/next.config.js | 16 - lib/spree/product/index.ts | 2 - lib/spree/product/use-price.tsx | 2 - lib/spree/product/use-search.tsx | 96 -- lib/spree/types/index.ts | 166 --- .../convert-spree-error-to-graph-ql-error.ts | 50 - .../utils/create-customized-fetch-fetcher.ts | 96 -- lib/spree/utils/create-empty-cart.ts | 22 - .../utils/create-get-absolute-image-url.ts | 22 - lib/spree/utils/expand-options.ts | 102 -- .../utils/force-isomorphic-config-values.ts | 42 - lib/spree/utils/get-image-url.ts | 38 - lib/spree/utils/get-media-gallery.ts | 21 - lib/spree/utils/get-product-path.ts | 7 - ...get-spree-sdk-method-from-endpoint-path.ts | 50 - lib/spree/utils/handle-token-errors.ts | 14 - lib/spree/utils/is-json-content-type.ts | 4 - lib/spree/utils/is-server.ts | 1 - lib/spree/utils/login.ts | 53 - .../utils/normalizations/normalize-cart.ts | 191 ---- .../utils/normalizations/normalize-page.ts | 42 - .../utils/normalizations/normalize-product.ts | 196 ---- .../utils/normalizations/normalize-user.ts | 16 - .../normalizations/normalize-wishlist.ts | 60 -- lib/spree/utils/require-config.ts | 14 - lib/spree/utils/sort-option-types.ts | 9 - lib/spree/utils/tokens/cart-token.ts | 16 - .../tokens/ensure-fresh-user-access-token.ts | 49 - lib/spree/utils/tokens/ensure-itoken.ts | 25 - lib/spree/utils/tokens/is-logged-in.ts | 9 - lib/spree/utils/tokens/revoke-user-tokens.ts | 45 - lib/spree/utils/tokens/user-token-response.ts | 50 - .../validate-all-products-taxonomy-id.ts | 13 - .../validations/validate-cookie-expire.ts | 19 - .../validate-images-option-filter.ts | 13 - .../validations/validate-images-quality.ts | 19 - .../utils/validations/validate-images-size.ts | 13 - .../validate-placeholder-image-url.ts | 13 - .../validate-products-prerender-count.ts | 19 - lib/spree/wishlist/index.ts | 3 - lib/spree/wishlist/use-add-item.tsx | 86 -- lib/spree/wishlist/use-remove-item.tsx | 75 -- lib/spree/wishlist/use-wishlist.tsx | 91 -- package.json | 1 + yarn.lock | 38 +- 161 files changed, 1353 insertions(+), 5468 deletions(-) delete mode 100644 .components/carousel.tsx delete mode 100644 .components/cart/actions.ts delete mode 100644 .components/cart/add-to-cart.tsx delete mode 100644 .components/cart/close-cart.tsx delete mode 100644 .components/cart/delete-item-button.tsx delete mode 100644 .components/cart/edit-item-quantity-button.tsx delete mode 100644 .components/cart/index.tsx delete mode 100644 .components/cart/modal.tsx delete mode 100644 .components/cart/open-cart.tsx delete mode 100644 .components/grid/index.tsx delete mode 100644 .components/grid/three-items.tsx delete mode 100644 .components/grid/tile.tsx delete mode 100644 .components/icons/logo.tsx delete mode 100644 .components/label.tsx delete mode 100644 .components/layout/footer-menu.tsx delete mode 100644 .components/layout/footer.tsx delete mode 100644 .components/layout/navbar/index.tsx delete mode 100644 .components/layout/navbar/mobile-menu.tsx delete mode 100644 .components/layout/navbar/search.tsx delete mode 100644 .components/layout/product-grid-items.tsx delete mode 100644 .components/layout/search/collections.tsx delete mode 100644 .components/layout/search/filter/dropdown.tsx delete mode 100644 .components/layout/search/filter/index.tsx delete mode 100644 .components/layout/search/filter/item.tsx delete mode 100644 .components/loading-dots.tsx delete mode 100644 .components/logo-square.tsx delete mode 100644 .components/opengraph-image.tsx delete mode 100644 .components/price.tsx delete mode 100644 .components/product/gallery.tsx delete mode 100644 .components/product/product-description.tsx delete mode 100644 .components/product/variant-selector.tsx delete mode 100644 .components/prose.tsx create mode 100644 app/diseases/page.tsx create mode 100644 app/ondemand/layout.tsx create mode 100644 app/ondemand/page.tsx create mode 100644 app/ondemand/products/[id]/page.tsx delete mode 100644 app/ondemand/products/page.tsx rename app/{[page] => page}/layout.tsx (100%) rename app/{[page] => page}/opengraph-image.tsx (100%) rename app/{[page] => page}/page.tsx (100%) create mode 100644 app/tests/[id]/page.tsx create mode 100644 app/tests/page.tsx create mode 100644 components/contact-us.tsx create mode 100644 components/diseases/diseases-and-conditions.tsx create mode 100644 components/diseases/specialities.tsx create mode 100644 components/home/quicklinks.tsx create mode 100644 components/latest-news.tsx create mode 100644 components/layout/base-layout.tsx create mode 100644 components/layout/header.tsx create mode 100644 components/layout/hero/hero-1.tsx create mode 100644 components/layout/hero/hero-2.tsx create mode 100644 components/layout/topbar.tsx create mode 100644 components/ondemand/navbar/index.tsx create mode 100644 components/science-innovation.tsx create mode 100644 components/tests/alphabet.tsx create mode 100644 components/tests/tests-table.tsx delete mode 100644 lib/spree/README-assets/screenshots.png delete mode 100644 lib/spree/README.md delete mode 100644 lib/spree/api/endpoints/cart/index.ts delete mode 100644 lib/spree/api/endpoints/catalog/index.ts delete mode 100644 lib/spree/api/endpoints/catalog/products.ts delete mode 100644 lib/spree/api/endpoints/checkout/get-checkout.ts delete mode 100644 lib/spree/api/endpoints/checkout/index.ts delete mode 100644 lib/spree/api/endpoints/customer/address.ts delete mode 100644 lib/spree/api/endpoints/customer/card.ts delete mode 100644 lib/spree/api/endpoints/customer/index.ts delete mode 100644 lib/spree/api/endpoints/login/index.ts delete mode 100644 lib/spree/api/endpoints/logout/index.ts delete mode 100644 lib/spree/api/endpoints/signup/index.ts delete mode 100644 lib/spree/api/endpoints/wishlist/index.tsx delete mode 100644 lib/spree/api/index.ts delete mode 100644 lib/spree/api/operations/get-all-pages.ts delete mode 100644 lib/spree/api/operations/get-all-product-paths.ts delete mode 100644 lib/spree/api/operations/get-all-products.ts delete mode 100644 lib/spree/api/operations/get-all-taxons.ts delete mode 100644 lib/spree/api/operations/get-customer-wishlist.ts delete mode 100644 lib/spree/api/operations/get-page.ts delete mode 100644 lib/spree/api/operations/get-product.ts delete mode 100644 lib/spree/api/operations/get-products.js delete mode 100644 lib/spree/api/operations/get-site-info.ts delete mode 100644 lib/spree/api/operations/index.ts delete mode 100644 lib/spree/api/utils/create-api-fetch.ts delete mode 100644 lib/spree/api/utils/fetch.ts delete mode 100644 lib/spree/auth/index.ts delete mode 100644 lib/spree/auth/use-login.tsx delete mode 100644 lib/spree/auth/use-logout.tsx delete mode 100644 lib/spree/auth/use-signup.tsx delete mode 100644 lib/spree/cart/index.ts delete mode 100644 lib/spree/cart/use-add-item.tsx delete mode 100644 lib/spree/cart/use-cart.tsx delete mode 100644 lib/spree/cart/use-remove-item.tsx delete mode 100644 lib/spree/cart/use-update-item.tsx delete mode 100644 lib/spree/checkout/use-checkout.tsx delete mode 100644 lib/spree/commerce.config.json delete mode 100644 lib/spree/customer/address/use-add-item.tsx delete mode 100644 lib/spree/customer/card/use-add-item.tsx delete mode 100644 lib/spree/customer/index.ts delete mode 100644 lib/spree/customer/use-customer.tsx delete mode 100644 lib/spree/errors/AccessTokenError.ts delete mode 100644 lib/spree/errors/MisconfigurationError.ts delete mode 100644 lib/spree/errors/MissingConfigurationValueError.ts delete mode 100644 lib/spree/errors/MissingLineItemVariantError.ts delete mode 100644 lib/spree/errors/MissingOptionValueError.ts delete mode 100644 lib/spree/errors/MissingPrimaryVariantError.ts delete mode 100644 lib/spree/errors/MissingProductError.ts delete mode 100644 lib/spree/errors/MissingSlugVariableError.ts delete mode 100644 lib/spree/errors/MissingVariantError.ts delete mode 100644 lib/spree/errors/RefreshTokenError.ts delete mode 100644 lib/spree/errors/SpreeResponseContentError.ts delete mode 100644 lib/spree/errors/SpreeSdkMethodFromEndpointPathError.ts delete mode 100644 lib/spree/errors/TokensNotRejectedError.ts delete mode 100644 lib/spree/errors/UserTokenResponseParseError.ts delete mode 100644 lib/spree/next.config.js delete mode 100644 lib/spree/product/index.ts delete mode 100644 lib/spree/product/use-price.tsx delete mode 100644 lib/spree/product/use-search.tsx delete mode 100644 lib/spree/types/index.ts delete mode 100644 lib/spree/utils/convert-spree-error-to-graph-ql-error.ts delete mode 100644 lib/spree/utils/create-customized-fetch-fetcher.ts delete mode 100644 lib/spree/utils/create-empty-cart.ts delete mode 100644 lib/spree/utils/create-get-absolute-image-url.ts delete mode 100644 lib/spree/utils/expand-options.ts delete mode 100644 lib/spree/utils/force-isomorphic-config-values.ts delete mode 100644 lib/spree/utils/get-image-url.ts delete mode 100644 lib/spree/utils/get-media-gallery.ts delete mode 100644 lib/spree/utils/get-product-path.ts delete mode 100644 lib/spree/utils/get-spree-sdk-method-from-endpoint-path.ts delete mode 100644 lib/spree/utils/handle-token-errors.ts delete mode 100644 lib/spree/utils/is-json-content-type.ts delete mode 100644 lib/spree/utils/is-server.ts delete mode 100644 lib/spree/utils/login.ts delete mode 100644 lib/spree/utils/normalizations/normalize-cart.ts delete mode 100644 lib/spree/utils/normalizations/normalize-page.ts delete mode 100644 lib/spree/utils/normalizations/normalize-product.ts delete mode 100644 lib/spree/utils/normalizations/normalize-user.ts delete mode 100644 lib/spree/utils/normalizations/normalize-wishlist.ts delete mode 100644 lib/spree/utils/require-config.ts delete mode 100644 lib/spree/utils/sort-option-types.ts delete mode 100644 lib/spree/utils/tokens/cart-token.ts delete mode 100644 lib/spree/utils/tokens/ensure-fresh-user-access-token.ts delete mode 100644 lib/spree/utils/tokens/ensure-itoken.ts delete mode 100644 lib/spree/utils/tokens/is-logged-in.ts delete mode 100644 lib/spree/utils/tokens/revoke-user-tokens.ts delete mode 100644 lib/spree/utils/tokens/user-token-response.ts delete mode 100644 lib/spree/utils/validations/validate-all-products-taxonomy-id.ts delete mode 100644 lib/spree/utils/validations/validate-cookie-expire.ts delete mode 100644 lib/spree/utils/validations/validate-images-option-filter.ts delete mode 100644 lib/spree/utils/validations/validate-images-quality.ts delete mode 100644 lib/spree/utils/validations/validate-images-size.ts delete mode 100644 lib/spree/utils/validations/validate-placeholder-image-url.ts delete mode 100644 lib/spree/utils/validations/validate-products-prerender-count.ts delete mode 100644 lib/spree/wishlist/index.ts delete mode 100644 lib/spree/wishlist/use-add-item.tsx delete mode 100644 lib/spree/wishlist/use-remove-item.tsx delete mode 100644 lib/spree/wishlist/use-wishlist.tsx diff --git a/.components/carousel.tsx b/.components/carousel.tsx deleted file mode 100644 index 286d4dfea..000000000 --- a/.components/carousel.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { getCollectionProducts } from 'lib/shopify'; -import Link from 'next/link'; -import { GridTileImage } from './grid/tile'; - -export async function Carousel() { - // Collections that start with `hidden-*` are hidden from the search page. - const products = await getCollectionProducts({ collection: 'hidden-homepage-carousel' }); - - if (!products?.length) return null; - - // Purposefully duplicating products to make the carousel loop and not run out of products on wide screens. - const carouselProducts = [...products, ...products, ...products]; - - return ( -
- -
- ); -} diff --git a/.components/cart/actions.ts b/.components/cart/actions.ts deleted file mode 100644 index fa2c34d37..000000000 --- a/.components/cart/actions.ts +++ /dev/null @@ -1,83 +0,0 @@ -'use server'; - -import { TAGS } from 'lib/constants'; -import { addToCart, createCart, getCart, removeFromCart, updateCart } from 'lib/shopify'; -import { revalidateTag } from 'next/cache'; -import { cookies } from 'next/headers'; - -export async function addItem(prevState: any, selectedVariantId: string | undefined) { - let cartId = cookies().get('cartId')?.value; - let cart; - - if (cartId) { - cart = await getCart(cartId); - } - - if (!cartId || !cart) { - cart = await createCart(); - cartId = cart.id; - cookies().set('cartId', cartId); - } - - if (!selectedVariantId) { - return 'Missing product variant ID'; - } - - try { - await addToCart(cartId, [{ merchandiseId: selectedVariantId, quantity: 1 }]); - revalidateTag(TAGS.cart); - } catch (e) { - return 'Error adding item to cart'; - } -} - -export async function removeItem(prevState: any, lineId: string) { - const cartId = cookies().get('cartId')?.value; - - if (!cartId) { - return 'Missing cart ID'; - } - - try { - await removeFromCart(cartId, [lineId]); - revalidateTag(TAGS.cart); - } catch (e) { - return 'Error removing item from cart'; - } -} - -export async function updateItemQuantity( - prevState: any, - payload: { - lineId: string; - variantId: string; - quantity: number; - } -) { - const cartId = cookies().get('cartId')?.value; - - if (!cartId) { - return 'Missing cart ID'; - } - - const { lineId, variantId, quantity } = payload; - - try { - if (quantity === 0) { - await removeFromCart(cartId, [lineId]); - revalidateTag(TAGS.cart); - return; - } - - await updateCart(cartId, [ - { - id: lineId, - merchandiseId: variantId, - quantity - } - ]); - revalidateTag(TAGS.cart); - } catch (e) { - return 'Error updating item quantity'; - } -} diff --git a/.components/cart/add-to-cart.tsx b/.components/cart/add-to-cart.tsx deleted file mode 100644 index 35329b03d..000000000 --- a/.components/cart/add-to-cart.tsx +++ /dev/null @@ -1,92 +0,0 @@ -'use client'; - -import { PlusIcon } from '@heroicons/react/24/outline'; -import clsx from 'clsx'; -import { addItem } from 'components/cart/actions'; -import LoadingDots from 'components/loading-dots'; -import { ProductVariant } from 'lib/shopify/types'; -import { useSearchParams } from 'next/navigation'; -import { useFormState, useFormStatus } from 'react-dom'; - -function SubmitButton({ - availableForSale, - selectedVariantId -}: { - availableForSale: boolean; - selectedVariantId: string | undefined; -}) { - const { pending } = useFormStatus(); - const buttonClasses = - 'relative flex w-full items-center justify-center rounded-full bg-blue-600 p-4 tracking-wide text-white'; - const disabledClasses = 'cursor-not-allowed opacity-60 hover:opacity-60'; - - if (!availableForSale) { - return ( - - ); - } - - if (!selectedVariantId) { - return ( - - ); - } - - return ( - - ); -} - -export function AddToCart({ - variants, - availableForSale -}: { - variants: ProductVariant[]; - availableForSale: boolean; -}) { - const [message, formAction] = useFormState(addItem, null); - const searchParams = useSearchParams(); - const defaultVariantId = variants.length === 1 ? variants[0]?.id : undefined; - const variant = variants.find((variant: ProductVariant) => - variant.selectedOptions.every( - (option) => option.value === searchParams.get(option.name.toLowerCase()) - ) - ); - const selectedVariantId = variant?.id || defaultVariantId; - const actionWithVariant = formAction.bind(null, selectedVariantId); - - return ( -
- -

- {message} -

- - ); -} diff --git a/.components/cart/close-cart.tsx b/.components/cart/close-cart.tsx deleted file mode 100644 index 515b94843..000000000 --- a/.components/cart/close-cart.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { XMarkIcon } from '@heroicons/react/24/outline'; -import clsx from 'clsx'; - -export default function CloseCart({ className }: { className?: string }) { - return ( -
- -
- ); -} diff --git a/.components/cart/delete-item-button.tsx b/.components/cart/delete-item-button.tsx deleted file mode 100644 index 814e1f389..000000000 --- a/.components/cart/delete-item-button.tsx +++ /dev/null @@ -1,50 +0,0 @@ -'use client'; - -import { XMarkIcon } from '@heroicons/react/24/outline'; -import clsx from 'clsx'; -import { removeItem } from 'components/cart/actions'; -import LoadingDots from 'components/loading-dots'; -import type { CartItem } from 'lib/shopify/types'; -import { useFormState, useFormStatus } from 'react-dom'; - -function SubmitButton() { - const { pending } = useFormStatus(); - - return ( - - ); -} - -export function DeleteItemButton({ item }: { item: CartItem }) { - const [message, formAction] = useFormState(removeItem, null); - const itemId = item.id; - const actionWithVariant = formAction.bind(null, itemId); - - return ( -
- -

- {message} -

- - ); -} diff --git a/.components/cart/edit-item-quantity-button.tsx b/.components/cart/edit-item-quantity-button.tsx deleted file mode 100644 index b743ab704..000000000 --- a/.components/cart/edit-item-quantity-button.tsx +++ /dev/null @@ -1,57 +0,0 @@ -'use client'; - -import { MinusIcon, PlusIcon } from '@heroicons/react/24/outline'; -import clsx from 'clsx'; -import { updateItemQuantity } from 'components/cart/actions'; -import LoadingDots from 'components/loading-dots'; -import type { CartItem } from 'lib/shopify/types'; -import { useFormState, useFormStatus } from 'react-dom'; - -function SubmitButton({ type }: { type: 'plus' | 'minus' }) { - const { pending } = useFormStatus(); - - return ( - - ); -} - -export function EditItemQuantityButton({ item, type }: { item: CartItem; type: 'plus' | 'minus' }) { - const [message, formAction] = useFormState(updateItemQuantity, null); - const payload = { - lineId: item.id, - variantId: item.merchandise.id, - quantity: type === 'plus' ? item.quantity + 1 : item.quantity - 1 - }; - const actionWithVariant = formAction.bind(null, payload); - - return ( -
- -

- {message} -

- - ); -} diff --git a/.components/cart/index.tsx b/.components/cart/index.tsx deleted file mode 100644 index 3e250ba93..000000000 --- a/.components/cart/index.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { getCart } from 'lib/shopify'; -import { cookies } from 'next/headers'; -import CartModal from './modal'; - -export default async function Cart() { - const cartId = cookies().get('cartId')?.value; - let cart; - - if (cartId) { - cart = await getCart(cartId); - } - - return ; -} diff --git a/.components/cart/modal.tsx b/.components/cart/modal.tsx deleted file mode 100644 index a30818940..000000000 --- a/.components/cart/modal.tsx +++ /dev/null @@ -1,191 +0,0 @@ -'use client'; - -import { Dialog, Transition } from '@headlessui/react'; -import { ShoppingCartIcon } from '@heroicons/react/24/outline'; -import Price from 'components/price'; -import { DEFAULT_OPTION } from 'lib/constants'; -import type { Cart } from 'lib/shopify/types'; -import { createUrl } from 'lib/utils'; -import Image from 'next/image'; -import Link from 'next/link'; -import { Fragment, useEffect, useRef, useState } from 'react'; -import CloseCart from './close-cart'; -import { DeleteItemButton } from './delete-item-button'; -import { EditItemQuantityButton } from './edit-item-quantity-button'; -import OpenCart from './open-cart'; - -type MerchandiseSearchParams = { - [key: string]: string; -}; - -export default function CartModal({ cart }: { cart: Cart | undefined }) { - const [isOpen, setIsOpen] = useState(false); - const quantityRef = useRef(cart?.totalQuantity); - const openCart = () => setIsOpen(true); - const closeCart = () => setIsOpen(false); - - useEffect(() => { - // Open cart modal when quantity changes. - if (cart?.totalQuantity !== quantityRef.current) { - // But only if it's not already open (quantity also changes when editing items in cart). - if (!isOpen) { - setIsOpen(true); - } - - // Always update the quantity reference - quantityRef.current = cart?.totalQuantity; - } - }, [isOpen, cart?.totalQuantity, quantityRef]); - - return ( - <> - - - - - - - - ); -} diff --git a/.components/cart/open-cart.tsx b/.components/cart/open-cart.tsx deleted file mode 100644 index fa8226ab5..000000000 --- a/.components/cart/open-cart.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { ShoppingCartIcon } from '@heroicons/react/24/outline'; -import clsx from 'clsx'; - -export default function OpenCart({ - className, - quantity -}: { - className?: string; - quantity?: number; -}) { - return ( -
- - - {quantity ? ( -
- {quantity} -
- ) : null} -
- ); -} diff --git a/.components/grid/index.tsx b/.components/grid/index.tsx deleted file mode 100644 index 92681555a..000000000 --- a/.components/grid/index.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import clsx from 'clsx'; - -function Grid(props: React.ComponentProps<'ul'>) { - return ( -
    - {props.children} -
- ); -} - -function GridItem(props: React.ComponentProps<'li'>) { - return ( -
  • - {props.children} -
  • - ); -} - -Grid.Item = GridItem; - -export default Grid; diff --git a/.components/grid/three-items.tsx b/.components/grid/three-items.tsx deleted file mode 100644 index 23b3f8991..000000000 --- a/.components/grid/three-items.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { GridTileImage } from 'components/grid/tile'; -import { getCollectionProducts } from 'lib/shopify'; -import type { Product } from 'lib/shopify/types'; -import Link from 'next/link'; - -function ThreeItemGridItem({ - item, - size, - priority -}: { - item: Product; - size: 'full' | 'half'; - priority?: boolean; -}) { - return ( -
    - - - -
    - ); -} - -export async function ThreeItemGrid() { - // Collections that start with `hidden-*` are hidden from the search page. - const homepageItems = await getCollectionProducts({ - collection: 'hidden-homepage-featured-items' - }); - - if (!homepageItems[0] || !homepageItems[1] || !homepageItems[2]) return null; - - const [firstProduct, secondProduct, thirdProduct] = homepageItems; - - return ( -
    - - - -
    - ); -} diff --git a/.components/grid/tile.tsx b/.components/grid/tile.tsx deleted file mode 100644 index f79a459f4..000000000 --- a/.components/grid/tile.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import clsx from 'clsx'; -import Image from 'next/image'; -import Label from '../label'; - -export function GridTileImage({ - isInteractive = true, - active, - label, - ...props -}: { - isInteractive?: boolean; - active?: boolean; - label?: { - title: string; - amount: string; - currencyCode: string; - position?: 'bottom' | 'center'; - }; -} & React.ComponentProps) { - return ( -
    - {props.src ? ( - // eslint-disable-next-line jsx-a11y/alt-text -- `alt` is inherited from `props`, which is being enforced with TypeScript - - ) : null} - {label ? ( -
    - ); -} diff --git a/.components/icons/logo.tsx b/.components/icons/logo.tsx deleted file mode 100644 index 46fa02464..000000000 --- a/.components/icons/logo.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import clsx from 'clsx'; - -export default function LogoIcon(props: React.ComponentProps<'svg'>) { - return ( - - - - - ); -} diff --git a/.components/label.tsx b/.components/label.tsx deleted file mode 100644 index 113afacb0..000000000 --- a/.components/label.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import clsx from 'clsx'; -import Price from './price'; - -const Label = ({ - title, - amount, - currencyCode, - position = 'bottom' -}: { - title: string; - amount: string; - currencyCode: string; - position?: 'bottom' | 'center'; -}) => { - return ( -
    -
    -

    {title}

    - -
    -
    - ); -}; - -export default Label; diff --git a/.components/layout/footer-menu.tsx b/.components/layout/footer-menu.tsx deleted file mode 100644 index 444406294..000000000 --- a/.components/layout/footer-menu.tsx +++ /dev/null @@ -1,46 +0,0 @@ -'use client'; - -import clsx from 'clsx'; -import { Menu } from 'lib/shopify/types'; -import Link from 'next/link'; -import { usePathname } from 'next/navigation'; -import { useEffect, useState } from 'react'; - -const FooterMenuItem = ({ item }: { item: Menu }) => { - const pathname = usePathname(); - const [active, setActive] = useState(pathname === item.path); - - useEffect(() => { - setActive(pathname === item.path); - }, [pathname, item.path]); - - return ( -
  • - - {item.title} - -
  • - ); -}; - -export default function FooterMenu({ menu }: { menu: Menu[] }) { - if (!menu.length) return null; - - return ( - - ); -} diff --git a/.components/layout/footer.tsx b/.components/layout/footer.tsx deleted file mode 100644 index ef1b1e8c7..000000000 --- a/.components/layout/footer.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import Link from 'next/link'; - -import FooterMenu from 'components/layout/footer-menu'; -import LogoSquare from 'components/logo-square'; -import { getMenu } from 'lib/shopify'; -import { Suspense } from 'react'; - -const { COMPANY_NAME, SITE_NAME } = process.env; - -export default async function Footer() { - const currentYear = new Date().getFullYear(); - const copyrightDate = 2023 + (currentYear > 2023 ? `-${currentYear}` : ''); - const skeleton = 'w-full h-6 animate-pulse rounded bg-neutral-200 dark:bg-neutral-700'; - const menu = await getMenu('next-js-frontend-footer-menu'); - const copyrightName = COMPANY_NAME || SITE_NAME || ''; - - return ( -
    -
    -
    - - - {SITE_NAME} - -
    - -
    -
    -
    -
    -
    -
    -
    - } - > - - - -
    -
    -
    -

    - © {copyrightDate} {copyrightName} - {copyrightName.length && !copyrightName.endsWith('.') ? '.' : ''} All rights reserved. -

    -
    -

    Designed in California

    -

    - - Crafted by ▲ Vercel - -

    -
    -
    -
    - ); -} diff --git a/.components/layout/navbar/index.tsx b/.components/layout/navbar/index.tsx deleted file mode 100644 index f7d2f6af9..000000000 --- a/.components/layout/navbar/index.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import Cart from 'components/cart'; -import OpenCart from 'components/cart/open-cart'; -import LogoSquare from 'components/logo-square'; -import { getMenu } from 'lib/shopify'; -import { Menu } from 'lib/shopify/types'; -import Link from 'next/link'; -import { Suspense } from 'react'; -import MobileMenu from './mobile-menu'; -import Search, { SearchSkeleton } from './search'; -const { SITE_NAME } = process.env; - -export default async function Navbar() { - const menu = await getMenu('next-js-frontend-header-menu'); - - return ( - - ); -} diff --git a/.components/layout/navbar/mobile-menu.tsx b/.components/layout/navbar/mobile-menu.tsx deleted file mode 100644 index 9091f93d3..000000000 --- a/.components/layout/navbar/mobile-menu.tsx +++ /dev/null @@ -1,100 +0,0 @@ -'use client'; - -import { Dialog, Transition } from '@headlessui/react'; -import Link from 'next/link'; -import { usePathname, useSearchParams } from 'next/navigation'; -import { Fragment, Suspense, useEffect, useState } from 'react'; - -import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline'; -import { Menu } from 'lib/shopify/types'; -import Search, { SearchSkeleton } from './search'; - -export default function MobileMenu({ menu }: { menu: Menu[] }) { - const pathname = usePathname(); - const searchParams = useSearchParams(); - const [isOpen, setIsOpen] = useState(false); - const openMobileMenu = () => setIsOpen(true); - const closeMobileMenu = () => setIsOpen(false); - - useEffect(() => { - const handleResize = () => { - if (window.innerWidth > 768) { - setIsOpen(false); - } - }; - window.addEventListener('resize', handleResize); - return () => window.removeEventListener('resize', handleResize); - }, [isOpen]); - - useEffect(() => { - setIsOpen(false); - }, [pathname, searchParams]); - - return ( - <> - - - - - - - - ); -} diff --git a/.components/layout/navbar/search.tsx b/.components/layout/navbar/search.tsx deleted file mode 100644 index 551d781c2..000000000 --- a/.components/layout/navbar/search.tsx +++ /dev/null @@ -1,57 +0,0 @@ -'use client'; - -import { MagnifyingGlassIcon } from '@heroicons/react/24/outline'; -import { createUrl } from 'lib/utils'; -import { useRouter, useSearchParams } from 'next/navigation'; - -export default function Search() { - const router = useRouter(); - const searchParams = useSearchParams(); - - function onSubmit(e: React.FormEvent) { - e.preventDefault(); - - const val = e.target as HTMLFormElement; - const search = val.search as HTMLInputElement; - const newParams = new URLSearchParams(searchParams.toString()); - - if (search.value) { - newParams.set('q', search.value); - } else { - newParams.delete('q'); - } - - router.push(createUrl('/search', newParams)); - } - - return ( -
    - -
    - -
    -
    - ); -} - -export function SearchSkeleton() { - return ( -
    - -
    - -
    -
    - ); -} diff --git a/.components/layout/product-grid-items.tsx b/.components/layout/product-grid-items.tsx deleted file mode 100644 index ea8a5ebf7..000000000 --- a/.components/layout/product-grid-items.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import Grid from 'components/grid'; -import { GridTileImage } from 'components/grid/tile'; -import { Product } from 'lib/shopify/types'; -import Link from 'next/link'; - -export default function ProductGridItems({ products }: { products: Product[] }) { - return ( - <> - {products.map((product) => ( - - - - - - ))} - - ); -} diff --git a/.components/layout/search/collections.tsx b/.components/layout/search/collections.tsx deleted file mode 100644 index c45833a39..000000000 --- a/.components/layout/search/collections.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import clsx from 'clsx'; -import { Suspense } from 'react'; - -import { getCollections } from 'lib/shopify'; -import FilterList from './filter'; - -async function CollectionList() { - const collections = await getCollections(); - return ; -} - -const skeleton = 'mb-3 h-4 w-5/6 animate-pulse rounded'; -const activeAndTitles = 'bg-neutral-800 dark:bg-neutral-300'; -const items = 'bg-neutral-400 dark:bg-neutral-700'; - -export default function Collections() { - return ( - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - } - > - - - ); -} diff --git a/.components/layout/search/filter/dropdown.tsx b/.components/layout/search/filter/dropdown.tsx deleted file mode 100644 index 31daa25ce..000000000 --- a/.components/layout/search/filter/dropdown.tsx +++ /dev/null @@ -1,64 +0,0 @@ -'use client'; - -import { usePathname, useSearchParams } from 'next/navigation'; -import { useEffect, useRef, useState } from 'react'; - -import { ChevronDownIcon } from '@heroicons/react/24/outline'; -import type { ListItem } from '.'; -import { FilterItem } from './item'; - -export default function FilterItemDropdown({ list }: { list: ListItem[] }) { - const pathname = usePathname(); - const searchParams = useSearchParams(); - const [active, setActive] = useState(''); - const [openSelect, setOpenSelect] = useState(false); - const ref = useRef(null); - - useEffect(() => { - const handleClickOutside = (event: MouseEvent) => { - if (ref.current && !ref.current.contains(event.target as Node)) { - setOpenSelect(false); - } - }; - - window.addEventListener('click', handleClickOutside); - return () => window.removeEventListener('click', handleClickOutside); - }, []); - - useEffect(() => { - list.forEach((listItem: ListItem) => { - if ( - ('path' in listItem && pathname === listItem.path) || - ('slug' in listItem && searchParams.get('sort') === listItem.slug) - ) { - setActive(listItem.title); - } - }); - }, [pathname, list, searchParams]); - - return ( -
    -
    { - setOpenSelect(!openSelect); - }} - className="flex w-full items-center justify-between rounded border border-black/30 px-4 py-2 text-sm dark:border-white/30" - > -
    {active}
    - -
    - {openSelect && ( -
    { - setOpenSelect(false); - }} - className="absolute z-40 w-full rounded-b-md bg-white p-4 shadow-md dark:bg-black" - > - {list.map((item: ListItem, i) => ( - - ))} -
    - )} -
    - ); -} diff --git a/.components/layout/search/filter/index.tsx b/.components/layout/search/filter/index.tsx deleted file mode 100644 index 11a7cd367..000000000 --- a/.components/layout/search/filter/index.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { SortFilterItem } from 'lib/constants'; -import { Suspense } from 'react'; -import FilterItemDropdown from './dropdown'; -import { FilterItem } from './item'; - -export type ListItem = SortFilterItem | PathFilterItem; -export type PathFilterItem = { title: string; path: string }; - -function FilterItemList({ list }: { list: ListItem[] }) { - return ( - <> - {list.map((item: ListItem, i) => ( - - ))} - - ); -} - -export default function FilterList({ list, title }: { list: ListItem[]; title?: string }) { - return ( - <> - - - ); -} diff --git a/.components/layout/search/filter/item.tsx b/.components/layout/search/filter/item.tsx deleted file mode 100644 index 3fce8e8a9..000000000 --- a/.components/layout/search/filter/item.tsx +++ /dev/null @@ -1,67 +0,0 @@ -'use client'; - -import clsx from 'clsx'; -import type { SortFilterItem } from 'lib/constants'; -import { createUrl } from 'lib/utils'; -import Link from 'next/link'; -import { usePathname, useSearchParams } from 'next/navigation'; -import type { ListItem, PathFilterItem } from '.'; - -function PathFilterItem({ item }: { item: PathFilterItem }) { - const pathname = usePathname(); - const searchParams = useSearchParams(); - const active = pathname === item.path; - const newParams = new URLSearchParams(searchParams.toString()); - const DynamicTag = active ? 'p' : Link; - - newParams.delete('q'); - - return ( -
  • - - {item.title} - -
  • - ); -} - -function SortFilterItem({ item }: { item: SortFilterItem }) { - const pathname = usePathname(); - const searchParams = useSearchParams(); - const active = searchParams.get('sort') === item.slug; - const q = searchParams.get('q'); - const href = createUrl( - pathname, - new URLSearchParams({ - ...(q && { q }), - ...(item.slug && item.slug.length && { sort: item.slug }) - }) - ); - const DynamicTag = active ? 'p' : Link; - - return ( -
  • - - {item.title} - -
  • - ); -} - -export function FilterItem({ item }: { item: ListItem }) { - return 'path' in item ? : ; -} diff --git a/.components/loading-dots.tsx b/.components/loading-dots.tsx deleted file mode 100644 index 10e642229..000000000 --- a/.components/loading-dots.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import clsx from 'clsx'; - -const dots = 'mx-[1px] inline-block h-1 w-1 animate-blink rounded-md'; - -const LoadingDots = ({ className }: { className: string }) => { - return ( - - - - - - ); -}; - -export default LoadingDots; diff --git a/.components/logo-square.tsx b/.components/logo-square.tsx deleted file mode 100644 index eccf5cba7..000000000 --- a/.components/logo-square.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import clsx from 'clsx'; -import LogoIcon from './icons/logo'; - -export default function LogoSquare({ size }: { size?: 'sm' | undefined }) { - return ( -
    - -
    - ); -} diff --git a/.components/opengraph-image.tsx b/.components/opengraph-image.tsx deleted file mode 100644 index 288e0bd50..000000000 --- a/.components/opengraph-image.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { ImageResponse } from 'next/og'; -import LogoIcon from './icons/logo'; - -export type Props = { - title?: string; -}; - -export default async function OpengraphImage(props?: Props): Promise { - const { title } = { - ...{ - title: process.env.SITE_NAME - }, - ...props - }; - - return new ImageResponse( - ( -
    -
    - -
    -

    {title}

    -
    - ), - { - width: 1200, - height: 630, - fonts: [ - { - name: 'Inter', - data: await fetch(new URL('../fonts/Inter-Bold.ttf', import.meta.url)).then((res) => - res.arrayBuffer() - ), - style: 'normal', - weight: 700 - } - ] - } - ); -} diff --git a/.components/price.tsx b/.components/price.tsx deleted file mode 100644 index e7090148d..000000000 --- a/.components/price.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import clsx from 'clsx'; - -const Price = ({ - amount, - className, - currencyCode = 'USD', - currencyCodeClassName -}: { - amount: string; - className?: string; - currencyCode: string; - currencyCodeClassName?: string; -} & React.ComponentProps<'p'>) => ( -

    - {`${new Intl.NumberFormat(undefined, { - style: 'currency', - currency: currencyCode, - currencyDisplay: 'narrowSymbol' - }).format(parseFloat(amount))}`} - {`${currencyCode}`} -

    -); - -export default Price; diff --git a/.components/product/gallery.tsx b/.components/product/gallery.tsx deleted file mode 100644 index 0b03557a5..000000000 --- a/.components/product/gallery.tsx +++ /dev/null @@ -1,99 +0,0 @@ -'use client'; - -import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/24/outline'; -import { GridTileImage } from 'components/grid/tile'; -import { createUrl } from 'lib/utils'; -import Image from 'next/image'; -import Link from 'next/link'; -import { usePathname, useSearchParams } from 'next/navigation'; - -export function Gallery({ images }: { images: { src: string; altText: string }[] }) { - const pathname = usePathname(); - const searchParams = useSearchParams(); - const imageSearchParam = searchParams.get('image'); - const imageIndex = imageSearchParam ? parseInt(imageSearchParam) : 0; - - const nextSearchParams = new URLSearchParams(searchParams.toString()); - const nextImageIndex = imageIndex + 1 < images.length ? imageIndex + 1 : 0; - nextSearchParams.set('image', nextImageIndex.toString()); - const nextUrl = createUrl(pathname, nextSearchParams); - - const previousSearchParams = new URLSearchParams(searchParams.toString()); - const previousImageIndex = imageIndex === 0 ? images.length - 1 : imageIndex - 1; - previousSearchParams.set('image', previousImageIndex.toString()); - const previousUrl = createUrl(pathname, previousSearchParams); - - const buttonClassName = - 'h-full px-6 transition-all ease-in-out hover:scale-110 hover:text-black dark:hover:text-white flex items-center justify-center'; - - return ( - <> -
    - {images[imageIndex] && ( - {images[imageIndex]?.altText - )} - - {images.length > 1 ? ( -
    -
    - - - -
    - - - -
    -
    - ) : null} -
    - - {images.length > 1 ? ( -
      - {images.map((image, index) => { - const isActive = index === imageIndex; - const imageSearchParams = new URLSearchParams(searchParams.toString()); - - imageSearchParams.set('image', index.toString()); - - return ( -
    • - - - -
    • - ); - })} -
    - ) : null} - - ); -} diff --git a/.components/product/product-description.tsx b/.components/product/product-description.tsx deleted file mode 100644 index 10232ae3d..000000000 --- a/.components/product/product-description.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { AddToCart } from 'components/cart/add-to-cart'; -import Price from 'components/price'; -import Prose from 'components/prose'; -import { Product } from 'lib/shopify/types'; -import { Suspense } from 'react'; -import { VariantSelector } from './variant-selector'; - -export function ProductDescription({ product }: { product: Product }) { - return ( - <> -
    -

    {product.title}

    -
    - -
    -
    - - - - - {product.descriptionHtml ? ( - - ) : null} - - - - - - ); -} diff --git a/.components/product/variant-selector.tsx b/.components/product/variant-selector.tsx deleted file mode 100644 index 9d47eb5c8..000000000 --- a/.components/product/variant-selector.tsx +++ /dev/null @@ -1,106 +0,0 @@ -'use client'; - -import clsx from 'clsx'; -import { ProductOption, ProductVariant } from 'lib/shopify/types'; -import { createUrl } from 'lib/utils'; -import { usePathname, useRouter, useSearchParams } from 'next/navigation'; - -type Combination = { - id: string; - availableForSale: boolean; - [key: string]: string | boolean; // ie. { color: 'Red', size: 'Large', ... } -}; - -export function VariantSelector({ - options, - variants -}: { - options: ProductOption[]; - variants: ProductVariant[]; -}) { - const router = useRouter(); - const pathname = usePathname(); - const searchParams = useSearchParams(); - const hasNoOptionsOrJustOneOption = - !options.length || (options.length === 1 && options[0]?.values.length === 1); - - if (hasNoOptionsOrJustOneOption) { - return null; - } - - const combinations: Combination[] = variants.map((variant) => ({ - id: variant.id, - availableForSale: variant.availableForSale, - // Adds key / value pairs for each variant (ie. "color": "Black" and "size": 'M"). - ...variant.selectedOptions.reduce( - (accumulator, option) => ({ ...accumulator, [option.name.toLowerCase()]: option.value }), - {} - ) - })); - - return options.map((option) => ( -
    -
    {option.name}
    -
    - {option.values.map((value) => { - const optionNameLowerCase = option.name.toLowerCase(); - - // Base option params on current params so we can preserve any other param state in the url. - const optionSearchParams = new URLSearchParams(searchParams.toString()); - - // Update the option params using the current option to reflect how the url *would* change, - // if the option was clicked. - optionSearchParams.set(optionNameLowerCase, value); - const optionUrl = createUrl(pathname, optionSearchParams); - - // In order to determine if an option is available for sale, we need to: - // - // 1. Filter out all other param state - // 2. Filter out invalid options - // 3. Check if the option combination is available for sale - // - // This is the "magic" that will cross check possible variant combinations and preemptively - // disable combinations that are not available. For example, if the color gray is only available in size medium, - // then all other sizes should be disabled. - const filtered = Array.from(optionSearchParams.entries()).filter(([key, value]) => - options.find( - (option) => option.name.toLowerCase() === key && option.values.includes(value) - ) - ); - const isAvailableForSale = combinations.find((combination) => - filtered.every( - ([key, value]) => combination[key] === value && combination.availableForSale - ) - ); - - // The option is active if it's in the url params. - const isActive = searchParams.get(optionNameLowerCase) === value; - - return ( - - ); - })} -
    -
    - )); -} diff --git a/.components/prose.tsx b/.components/prose.tsx deleted file mode 100644 index f910d2296..000000000 --- a/.components/prose.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import clsx from 'clsx'; -import type { FunctionComponent } from 'react'; - -interface TextProps { - html: string; - className?: string; -} - -const Prose: FunctionComponent = ({ html, className }) => { - return ( -
    - ); -}; - -export default Prose; diff --git a/app/diseases/page.tsx b/app/diseases/page.tsx new file mode 100644 index 000000000..22f520cbe --- /dev/null +++ b/app/diseases/page.tsx @@ -0,0 +1,23 @@ +import DiseasesAndConditions from 'components/diseases/diseases-and-conditions'; +import Specialities from 'components/diseases/specialities'; + +import spree from '@commerce/index'; + +async function getTaxons() { + const res = await spree.taxons.list({}); + + if (res.isFail()) throw new Error('Failed to fetch data'); + + return res.success().data; +} + +export default async function DiseasesPage() { + const taxons = await getTaxons(); + + return ( +
    + + +
    + ); +} diff --git a/app/layout.tsx b/app/layout.tsx index 1e17f31d3..3a58246c4 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,42 +1,15 @@ +import Footer from 'components/layout/footer'; import Navbar from 'components/layout/navbar'; -import { GeistSans } from 'geist/font/sans'; -import { ensureStartsWith } from 'lib/utils'; import { ReactNode } from 'react'; import './globals.css'; -const { TWITTER_CREATOR, TWITTER_SITE, SITE_NAME } = process.env; -const baseUrl = process.env.NEXT_PUBLIC_VERCEL_URL - ? `https://${process.env.NEXT_PUBLIC_VERCEL_URL}` - : 'http://localhost:3000'; -const twitterCreator = TWITTER_CREATOR ? ensureStartsWith(TWITTER_CREATOR, '@') : undefined; -const twitterSite = TWITTER_SITE ? ensureStartsWith(TWITTER_SITE, 'https://') : undefined; - -export const metadata = { - metadataBase: new URL(baseUrl), - title: { - default: SITE_NAME!, - template: `%s | ${SITE_NAME}` - }, - robots: { - follow: true, - index: true - }, - ...(twitterCreator && - twitterSite && { - twitter: { - card: 'summary_large_image', - creator: twitterCreator, - site: twitterSite - } - }) -}; - export default async function RootLayout({ children }: { children: ReactNode }) { return ( - - + +
    {children}
    +