4
0
forked from crowetic/commerce
Jakub Neander 3b2bf654fe
Updated Saleor Provider (#356)
* Initial work, copied from the Shopify provider

* Added basis setup and type generation for the products queries

* refactor: adjust the types

* task: relax the Node.js constraint

* fix: page/product properties

* disable unknown fields

* mention Saleor in the README

* setup debugging for Next.js

* Check nextjs-commerce bug if no images are added for a product

* fix: client/server pecularities for env visibility

Must prefix with `NEXT_PUBLIC_` so that the API URL is
visible on the client

* re: make search work with Saleor API (WIP)

* task: update deps

* task: move to Webpack 5.x

* saleor: initial cart integration

* update deps

* saleor: shall the cart appear!

* task: remove deprecated packages

* saleor: adding/removing from the cart

* saleor: preliminary signup process

* saleor: fix the prices in the cart

* update deps

* update deps

* Added the options for a variant to the product page

* Mapped options to variants

* Mapped options to variants

* saleor: refine the auth process

* saleor: remove unused code

* saleor: handle customer find via refresh

temporary solution

* saleor: update deps

* saleor: fix the session handling

* saleor: fix the variants

* saleor: simplify the naming for GraphQL statements

* saleor: fix the type for collection

* saleor: arrange the error codes

* saleor: integrate collections

* saleor: fix product sorting

* saleor: set cookie location

* saleor: update the schema

* saleor: attach checkout to customer

* saleor: fix the checkout flow

* saleor: unify GraphQL naming approach

* task: update deps

* Add the env variables for saleor to the template

* task: prettier

* saleor: stub API for build/typescript compilation

thanks @cond0r

* task: temporarily disable for the `build`

* saleor: refactor GraphQL queries

* saleor: adjust the config

* task: update dependencies

* revert: Next.js to `10.0.9`

* saleor: fix the checkout fetch query

* task: update dependencies

* saleor: adapt for displaying featured products

* saleor: update the provider structure

* saleor: make the home page representable

* feature/cart: display the variant name (cond)

Co-authored-by: Patryk Zawadzki <patrys@room-303.com>
Co-authored-by: royderks <10717410+royderks@users.noreply.github.com>
2021-06-10 01:46:28 -05:00

168 lines
4.6 KiB
TypeScript

import { ChangeEvent, useEffect, useState } from 'react'
import cn from 'classnames'
import Image from 'next/image'
import Link from 'next/link'
import s from './CartItem.module.css'
import { Trash, Plus, Minus } from '@components/icons'
import { useUI } from '@components/ui/context'
import type { LineItem } from '@commerce/types/cart'
import usePrice from '@framework/product/use-price'
import useUpdateItem from '@framework/cart/use-update-item'
import useRemoveItem from '@framework/cart/use-remove-item'
type ItemOption = {
name: string
nameId: number
value: string
valueId: number
}
const CartItem = ({
item,
currencyCode,
...rest
}: {
item: LineItem
currencyCode: string
}) => {
const { closeSidebarIfPresent } = useUI()
const { price } = usePrice({
amount: item.variant.price * item.quantity,
baseAmount: item.variant.listPrice * item.quantity,
currencyCode,
})
const updateItem = useUpdateItem({ item })
const removeItem = useRemoveItem()
const [quantity, setQuantity] = useState<number | ''>(item.quantity)
const [removing, setRemoving] = useState(false)
const updateQuantity = async (val: number) => {
await updateItem({ quantity: val })
}
const handleQuantity = (e: ChangeEvent<HTMLInputElement>) => {
const val = !e.target.value ? '' : Number(e.target.value)
if (!val || (Number.isInteger(val) && val >= 0)) {
setQuantity(val)
}
}
const handleBlur = () => {
const val = Number(quantity)
if (val !== item.quantity) {
updateQuantity(val)
}
}
const increaseQuantity = (n = 1) => {
const val = Number(quantity) + n
if (Number.isInteger(val) && val >= 0) {
setQuantity(val)
updateQuantity(val)
}
}
const handleRemove = async () => {
setRemoving(true)
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(item)
} catch (error) {
setRemoving(false)
}
}
// TODO: Add a type for this
const options = (item as any).options
useEffect(() => {
// Reset the quantity state if the item quantity changes
if (item.quantity !== Number(quantity)) {
setQuantity(item.quantity)
}
}, [item.quantity])
return (
<li
className={cn('flex flex-row space-x-8 py-8', {
'opacity-75 pointer-events-none': removing,
})}
{...rest}
>
<div className="w-16 h-16 bg-violet relative overflow-hidden cursor-pointer">
<Link href={`/product/${item.path}`}>
<Image
onClick={() => closeSidebarIfPresent()}
className={s.productImage}
width={150}
height={150}
src={item.variant.image!.url}
alt={item.variant.image!.altText}
unoptimized
/>
</Link>
</div>
<div className="flex-1 flex flex-col text-base">
<Link href={`/product/${item.path}`}>
<span
onClick={() => closeSidebarIfPresent()}
>
<div
className="font-bold text-lg cursor-pointer leading-6"
>
{item.name}
</div>
{item.variant ? <span> {item.variant.name}</span> : ""}
</span>
</Link>
{options && options.length > 0 ? (
<div className="">
{options.map((option: ItemOption, i: number) => (
<span
key={`${item.id}-${option.name}`}
className="text-sm font-semibold text-accents-7"
>
{option.value}
{i === options.length - 1 ? '' : ', '}
</span>
))}
</div>
) : null}
<div className="flex items-center mt-3">
<button type="button" onClick={() => increaseQuantity(-1)}>
<Minus width={18} height={18} />
</button>
<label>
<input
type="number"
max={99}
min={0}
className={s.quantity}
value={quantity}
onChange={handleQuantity}
onBlur={handleBlur}
/>
</label>
<button type="button" onClick={() => increaseQuantity(1)}>
<Plus width={18} height={18} />
</button>
</div>
</div>
<div className="flex flex-col justify-between space-y-2 text-base">
<span>{price}</span>
<button
className="flex justify-end outline-none"
onClick={handleRemove}
>
<Trash />
</button>
</div>
</li>
)
}
export default CartItem