4
0
forked from crowetic/commerce
This commit is contained in:
Belen Curcio 2021-01-14 12:58:41 -03:00
parent fc34856e50
commit 7f70cfd868
15 changed files with 124 additions and 67 deletions

View File

@ -12,7 +12,7 @@ const CartItem = ({
item,
currencyCode,
}: {
item: any
item: CartItem
currencyCode: string
}) => {
const { price } = usePrice({
@ -55,7 +55,7 @@ const CartItem = ({
try {
// If this action succeeds then there's no need to do `setRemoving(true)`
// because the component will be removed from the view
await removeItem({ id: item.id })
await removeItem({ id: String(item.id) })
} catch (error) {
setRemoving(false)
}
@ -77,16 +77,14 @@ const CartItem = ({
<div className="w-16 h-16 bg-violet relative overflow-hidden">
<Image
className={s.productImage}
src={item.image_url}
width={150}
height={150}
alt="Product Image"
// The cart item image is already optimized and very small in size
src={item.images[0].url}
alt={item.images[0].alt}
unoptimized
/>
</div>
<div className="flex-1 flex flex-col text-base">
{/** TODO: Replace this. No `path` found at Cart */}
<Link href={`/product/${item.url.split('/')[3]}`}>
<span className="font-bold mb-5 text-lg cursor-pointer">
{item.name}
@ -115,7 +113,10 @@ const CartItem = ({
</div>
<div className="flex flex-col justify-between space-y-2 text-base">
<span>{price}</span>
<button className="flex justify-end" onClick={handleRemove}>
<button
className="flex justify-end outline-none"
onClick={handleRemove}
>
<Trash />
</button>
</div>

View File

@ -16,19 +16,17 @@ const CartSidebarView: FC = () => {
const { price: subTotal } = usePrice(
data && {
amount: data.base_amount,
currencyCode: data.currency.code,
currencyCode: data.currency?.code || 'USD',
}
)
const { price: total } = usePrice(
data && {
amount: data.cart_amount,
currencyCode: data.currency.code,
currencyCode: data.currency?.code || 'USD',
}
)
const handleClose = () => closeSidebar()
const items = data?.line_items.physical_items ?? []
const error = null
const success = null
@ -95,7 +93,7 @@ const CartSidebarView: FC = () => {
My Cart
</h2>
<ul className="py-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-accents-3 border-t border-accents-3">
{items.map((item: any) => (
{data.products.map((item: any) => (
<CartItem
key={item.id}
item={item}

View File

@ -146,7 +146,7 @@ const ProductView: FC<Props> = ({ product }) => {
className={s.button}
onClick={addToCart}
loading={loading}
disabled={!variant}
disabled={!variant && product.options.length > 0}
>
Add to Cart
</Button>

View File

@ -1,3 +1,4 @@
import { Product } from 'framework/types'
import getAllProducts, { ProductEdge } from '../../operations/get-all-products'
import type { ProductsHandlers } from '../products'
@ -6,6 +7,7 @@ const SORT: { [key: string]: string | undefined } = {
trending: 'total_sold',
price: 'price',
}
const LIMIT = 12
// Return current cart info
@ -44,21 +46,25 @@ const getProducts: ProductsHandlers['getProducts'] = async ({
const { data } = await config.storeApiFetch<{ data: { id: number }[] }>(
url.pathname + url.search
)
const entityIds = data.map((p) => p.id)
const found = entityIds.length > 0
// We want the GraphQL version of each product
const graphqlData = await getAllProducts({
variables: { first: LIMIT, entityIds },
config,
})
// Put the products in an object that we can use to get them by id
const productsById = graphqlData.products.reduce<{
[k: number]: ProductEdge
[k: number]: Product
}>((prods, p) => {
prods[p.node.entityId] = p
prods[p.id] = p
return prods
}, {})
const products: ProductEdge[] = found ? [] : graphqlData.products
const products: Product[] = found ? [] : graphqlData.products
// Populate the products array with the graphql products, in the order
// assigned by the list of entity ids

View File

@ -4,11 +4,11 @@ import createApiHandler, {
BigcommerceHandler,
} from '../utils/create-api-handler'
import { BigcommerceApiError } from '../utils/errors'
import type { ProductEdge } from '../operations/get-all-products'
import getProducts from './handlers/get-products'
import { Product } from 'framework/types'
export type SearchProductsData = {
products: ProductEdge[]
products: Product[]
found: boolean
}

View File

@ -127,7 +127,7 @@ async function getAllProducts({
})
}
return { products: products.map(({ node }) => normalizeProduct(node)) }
return { products: products.map(({ node }) => normalizeProduct(node as any)) }
}
export default getAllProducts

View File

@ -111,7 +111,7 @@ async function getProduct({
setProductLocaleMeta(product)
}
return { product: normalizeProduct(product) }
return { product: normalizeProduct(product as any) }
}
return {}

View File

@ -1,4 +1,5 @@
import { Product as BCProduct } from '@framework/schema'
import { Cart, CartItem, Product } from '../../types'
import { Product as BigCommerceProduct } from '@framework/schema'
function normalizeProductOption({
node: {
@ -14,7 +15,7 @@ function normalizeProductOption({
}
}
export function normalizeProduct(productNode: BCProduct): Product {
export function normalizeProduct(productNode: BigCommerceProduct): Product {
const {
entityId: id,
images,
@ -67,12 +68,47 @@ export function normalizeProduct(productNode: BCProduct): Product {
}
}
export function normalizeCart({ data, ...rest }: any) {
export function normalizeCart({ data, ...rest }: any): Cart {
return {
...rest,
data: {
products: data?.line_items?.physical_items ?? [],
products: data?.line_items?.physical_items.map(itemsToProducts) ?? [],
...data,
},
}
}
function itemsToProducts({
id,
name,
quantity,
product_id,
variant_id,
image_url,
list_price,
sale_price,
extended_list_price,
extended_sale_price,
...rest
}: any): CartItem {
return {
id,
name,
prices: {
listPrice: list_price,
salePrice: sale_price,
extendedListPrice: extended_list_price,
extendedSalePrice: extended_sale_price,
},
images: [
{
alt: name,
url: image_url,
},
],
productId: product_id,
variantId: variant_id,
quantity,
...rest,
}
}

19
framework/types.d.ts vendored
View File

@ -1,3 +1,5 @@
import { CartItem } from '@components/cart'
interface Entity {
id: string | number
[prop: string]: any
@ -12,6 +14,7 @@ interface Product extends Entity {
variants: ProductVariant[]
price: ProductPrice
options: ProductOption[]
sku?: string
}
interface ProductOption extends Entity {
@ -37,8 +40,11 @@ interface ProductVariant {
interface ProductPrice {
value: number
currencyCode: 'USD' | 'ARS' | string | undefined
retailValue?: number
saleValue?: number
retailPrice?: number
salePrice?: number
listPrice?: number
extendedSalePrice?: number
extendedListPrice?: number
}
interface Cart extends Entity {
@ -46,7 +52,14 @@ interface Cart extends Entity {
currency: { code: string }
taxIncluded?: boolean
totalAmmount: number | string
products: Pick<Product, 'id' | 'name' | 'prices'>[]
products: Pick<Product, 'id' | 'name' | 'prices'> & CartItem[]
}
interface CartItem extends Entity {
quantity: number
productId: Product['id']
variantId: ProductVariant['id']
images: ProductImage[]
}
interface Wishlist extends Entity {

View File

@ -60,6 +60,7 @@
"next": "^10.0.5",
"next-seo": "^4.11.0",
"next-themes": "^0.0.4",
"normalizr": "^3.6.1",
"postcss-nesting": "^7.0.1",
"react": "^16.14.0",
"react-dom": "^16.14.0",

View File

@ -29,20 +29,18 @@ export default function Cart() {
return <div>Loading</div>
}
console.log('Cart Data', data)
// const { price: subTotal } = usePrice(
// data && {
// amount: data.base_amount,
// currencyCode: data.currency.code,
// }
// )
// const { price: total } = usePrice(
// data && {
// amount: data.cart_amount,
// currencyCode: data.currency.code,
// }
// )
const { price: subTotal } = usePrice(
data && {
amount: data.base_amount,
currencyCode: data.currency.code,
}
)
const { price: total } = usePrice(
data && {
amount: data.cart_amount,
currencyCode: data.currency.code,
}
)
// const items = data?.line_items.physical_items ?? []

View File

@ -1,7 +0,0 @@
import { Layout } from '@components/common'
export default function Home() {
return <>Hello</>
}
Home.Layout = Layout

View File

@ -3,16 +3,29 @@ import type { GetStaticPropsContext, InferGetStaticPropsType } from 'next'
import Link from 'next/link'
import { useState } from 'react'
import { useRouter } from 'next/router'
import { getConfig } from '@framework/api'
import getAllPages from '@framework/api/operations/get-all-pages'
import getSiteInfo from '@framework/api/operations/get-site-info'
import useSearch from '@framework/product/use-search'
import { Layout } from '@components/common'
import { ProductCard } from '@components/product'
import { Container, Grid, Skeleton } from '@components/ui'
import { getConfig } from '@framework/api'
import getAllPages from '@framework/api/operations/get-all-pages'
import getSiteInfo from '@framework/api/operations/get-site-info'
import useSearch from '@framework/product/use-search'
import rangeMap from '@lib/range-map'
// TODO(bc) Remove this. This should come from the API
import getSlug from '@lib/get-slug'
// TODO (bc) : Remove or standarize this.
const SORT = Object.entries({
'latest-desc': 'Latest arrivals',
'trending-desc': 'Trending',
'price-asc': 'Price: Low to high',
'price-desc': 'Price: High to low',
})
import {
filterQuery,
getCategoryPath,
@ -27,19 +40,11 @@ export async function getStaticProps({
const config = getConfig({ locale })
const { pages } = await getAllPages({ config, preview })
const { categories, brands } = await getSiteInfo({ config, preview })
return {
props: { pages, categories, brands },
}
}
const SORT = Object.entries({
'latest-desc': 'Latest arrivals',
'trending-desc': 'Trending',
'price-asc': 'Price: Low to high',
'price-desc': 'Price: High to low',
})
export default function Search({
categories,
brands,
@ -76,7 +81,6 @@ export default function Search({
} else {
setToggleFilter(!toggleFilter)
}
setActiveFilter(filter)
}
@ -333,14 +337,16 @@ export default function Search({
{data ? (
<Grid layout="normal">
{data.products.map(({ node }) => (
{data.products.map((product) => (
<ProductCard
variant="simple"
key={node.path}
key={product.path}
className="animated fadeIn"
product={node}
imgWidth={480}
imgHeight={480}
product={product}
imgProps={{
width: 480,
height: 480,
}}
/>
))}
</Grid>

View File

@ -28,7 +28,7 @@
},
"include": [
"next-env.d.ts",
"./framework/types.d.ts",
"framework/types.d.ts",
"**/*.ts",
"**/*.tsx",
"**/*.js"

View File

@ -5195,6 +5195,11 @@ normalize.css@^8.0.1:
resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-8.0.1.tgz#9b98a208738b9cc2634caacbc42d131c97487bf3"
integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==
normalizr@^3.6.1:
version "3.6.1"
resolved "https://registry.yarnpkg.com/normalizr/-/normalizr-3.6.1.tgz#d367ab840e031ff382141b8d81ce279292ff69fe"
integrity sha512-8iEmqXmPtll8PwbEFrbPoDxVw7MKnNvt3PZzR2Xvq9nggEEOgBlNICPXYzyZ4w4AkHUzCU998mdatER3n2VaMA==
npmlog@^4.0.1, npmlog@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"