corrections

This commit is contained in:
Christos Emmanouilidis 2022-04-20 10:31:26 +03:00
parent e81c0fb166
commit a0ab4c826c
22 changed files with 3571 additions and 1192 deletions

4
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,4 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
}

3333
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -15,15 +15,16 @@
"generate": "graphql-codegen --config codegen.yml" "generate": "graphql-codegen --config codegen.yml"
}, },
"devDependencies": { "devDependencies": {
"@graphql-codegen/cli": "2.6.2",
"@graphql-codegen/introspection": "2.1.1",
"@graphql-codegen/typescript": "2.4.8",
"@graphql-codegen/typescript-document-nodes": "2.2.8",
"@graphql-codegen/typescript-graphql-files-modules": "2.1.1",
"@graphql-codegen/typescript-operations": "2.3.5",
"@types/node": "^17.0.24",
"husky": "^7.0.4", "husky": "^7.0.4",
"prettier": "^2.5.1", "prettier": "^2.5.1",
"turbo": "^1.1.2", "turbo": "^1.1.2"
"@graphql-codegen/typescript-document-nodes": "2.2.8",
"@graphql-codegen/typescript": "2.4.8",
"@graphql-codegen/typescript-operations": "2.3.5",
"@graphql-codegen/typescript-graphql-files-modules": "2.1.1",
"@graphql-codegen/introspection": "2.1.1",
"@graphql-codegen/cli": "2.6.2"
}, },
"husky": { "husky": {
"hooks": { "hooks": {

View File

@ -34,13 +34,13 @@ const wishlistEndpoint: GetAPISchema<
// Add an item to the wishlist // Add an item to the wishlist
if (req.method === 'POST') { if (req.method === 'POST') {
const body = { ...req.body.variables, customerToken } const body = { ...req.body, customerToken }
return await handlers['addItem']({ ...ctx, body }) return await handlers['addItem']({ ...ctx, body })
} }
// Remove an item from the wishlist // Remove an item from the wishlist
if (req.method === 'DELETE') { if (req.method === 'DELETE') {
const body = { ...req.body.variables, customerToken } const body = { ...req.body, customerToken }
return await handlers['removeItem']({ ...ctx, body }) return await handlers['removeItem']({ ...ctx, body })
} }
} catch (error) { } catch (error) {

View File

@ -27,10 +27,11 @@ export type AddItemHook<T extends WishlistTypes = WishlistTypes> = {
} }
export type RemoveItemHook<T extends WishlistTypes = WishlistTypes> = { export type RemoveItemHook<T extends WishlistTypes = WishlistTypes> = {
data: T['wishlist'] data: T['wishlist'] | null
body: { item: T['itemBody'] } body: { itemId: string }
fetcherInput: { item: T['itemBody'] } fetcherInput: { itemId: string }
actionInput: T['itemBody'] actionInput: { id: string }
input: { wishlist?: { includeProducts?: boolean } }
} }
export type WishlistSchema<T extends WishlistTypes = WishlistTypes> = { export type WishlistSchema<T extends WishlistTypes = WishlistTypes> = {

View File

@ -2,4 +2,4 @@ COMMERCE_PROVIDER=shopify
NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN= NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN=
NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN= NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN=
NEXT_PUBLIC_SHOPIFY_ADMIN_ACCESS_TOKEN= SHOPIFY_ADMIN_ACCESS_TOKEN=

View File

@ -2,7 +2,7 @@
"schema": { "schema": {
"https://${NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN}/admin/api/2022-01/graphql.json": { "https://${NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN}/admin/api/2022-01/graphql.json": {
"headers": { "headers": {
"X-Shopify-Access-Token": "${NEXT_PUBLIC_SHOPIFY_ADMIN_ACCESS_TOKEN}" "X-Shopify-Access-Token": "${SHOPIFY_ADMIN_ACCESS_TOKEN}"
} }
} }
}, },

View File

@ -9,7 +9,7 @@ import { WishlistItem } from './../../../types/wishlist'
const addWishlistItem: WishlistEndpoint['handlers']['addItem'] = async ({ const addWishlistItem: WishlistEndpoint['handlers']['addItem'] = async ({
res, res,
body: { item, customerToken }, body: { variables: item, customerToken },
config, config,
commerce, commerce,
}) => { }) => {

View File

@ -5,10 +5,11 @@ import type {
MutationCustomerUpdateArgs, MutationCustomerUpdateArgs,
Mutation, Mutation,
} from '../../../../admin-schema' } from '../../../../admin-schema'
import { debug } from 'util'
const removeWishlistItem: WishlistEndpoint['handlers']['removeItem'] = async ({ const removeWishlistItem: WishlistEndpoint['handlers']['removeItem'] = async ({
res, res,
body: { item, customerToken }, body: { variables: item, customerToken },
config, config,
commerce, commerce,
}) => { }) => {
@ -36,7 +37,15 @@ const removeWishlistItem: WishlistEndpoint['handlers']['removeItem'] = async ({
}) })
const WishlistItems: WishlistItemBody[] = wishlist?.items const WishlistItems: WishlistItemBody[] = wishlist?.items
?.filter((wishlistItem) => wishlistItem.productId !== item.productId) ?.filter((wishlistItem) => {
if (
wishlistItem.productId === item.itemId &&
wishlistItem.variantId === item.itemVariantId
) {
return false
}
return true
})
.map(({ product, ...rest }) => { .map(({ product, ...rest }) => {
return rest return rest
})! })!

View File

@ -13,4 +13,4 @@ export const ADMIN_API_URL = `https://${STORE_DOMAIN}/admin/api/2022-01/graphql.
export const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN export const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN
export const ADMIN_ACCESS_TOKEN = export const ADMIN_ACCESS_TOKEN =
process.env.NEXT_PUBLIC_SHOPIFY_ADMIN_ACCESS_TOKEN process.env.SHOPIFY_ADMIN_ACCESS_TOKEN

View File

@ -19,7 +19,33 @@ export type WishlistTypes = {
customer: Customer customer: Customer
} }
export type WishlistSchema = Core.WishlistSchema<WishlistTypes> export type RemoveItemHook<T extends WishlistTypes = WishlistTypes> =
Core.RemoveItemHook & {
fetcherInput: { itemVariantId?: string | number }
actionInput: { itemVariantId?: string | number }
}
export type WishlistSchema<T extends WishlistTypes = WishlistTypes> = {
endpoint: {
options: {}
handlers: {
getWishlist: Core.GetWishlistHook<T> & {
data: T['wishlist'] | null
body: { customerToken?: string }
}
addItem: Core.AddItemHook<T> & {
body: { variables?: T['itemBody']; customerToken?: string }
}
removeItem: RemoveItemHook<T> & {
body: {
variables?: { itemId: string; itemVariantId?: string | number }
customerToken?: string
}
}
}
}
}
export type GetCustomerWishlistOperation = export type GetCustomerWishlistOperation =
Core.GetCustomerWishlistOperation<WishlistTypes> Core.GetCustomerWishlistOperation<WishlistTypes>

View File

@ -14,7 +14,7 @@ export const handler: MutationHook<AddItemHook> = {
url: '/api/wishlist', url: '/api/wishlist',
method: 'POST', method: 'POST',
}, },
async fetcher({ input: item, options, fetch }) { async fetcher({ input: { item }, options, fetch }) {
const data = await fetch({ ...options, variables: item }) const data = await fetch({ ...options, variables: item })
return data return data

View File

@ -16,8 +16,8 @@ export const handler: MutationHook<RemoveItemHook> = {
url: '/api/wishlist', url: '/api/wishlist',
method: 'DELETE', method: 'DELETE',
}, },
async fetcher({ input: item, options, fetch }) { async fetcher({ input, options, fetch }) {
const data = await fetch({ ...options, variables: item }) const data = await fetch({ ...options, variables: input })
return data return data
}, },
@ -28,7 +28,7 @@ export const handler: MutationHook<RemoveItemHook> = {
const { mutate } = useWishlist() const { mutate } = useWishlist()
return useCallback( return useCallback(
async function removeItem(item) { async function removeItem(input) {
if (!customer) { if (!customer) {
// A signed customer is required in order to have a wishlist // A signed customer is required in order to have a wishlist
throw new CommerceError({ throw new CommerceError({
@ -38,7 +38,9 @@ export const handler: MutationHook<RemoveItemHook> = {
// TODO: add validations before doing the fetch // TODO: add validations before doing the fetch
const data = await fetch({ input: { item } }) const data = await fetch({
input: { itemId: input.id, itemVariantId: input.itemVariantId },
})
await mutate() await mutate()
return data return data
}, },

View File

@ -1,7 +1,7 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "esnext", "target": "es6",
"module": "esnext", "module": "es6",
"outDir": "dist", "outDir": "dist",
"baseUrl": "src", "baseUrl": "src",
"lib": ["dom", "dom.iterable", "esnext"], "lib": ["dom", "dom.iterable", "esnext"],

View File

@ -24,7 +24,7 @@ BIGCOMMERCE_STORE_API_CLIENT_SECRET=
NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN= NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN=
NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN= NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN=
NEXT_PUBLIC_SHOPIFY_ADMIN_ACCESS_TOKEN= SHOPIFY_ADMIN_ACCESS_TOKEN=
NEXT_PUBLIC_SWELL_STORE_ID= NEXT_PUBLIC_SWELL_STORE_ID=
NEXT_PUBLIC_SWELL_PUBLIC_KEY= NEXT_PUBLIC_SWELL_PUBLIC_KEY=

View File

@ -2,8 +2,8 @@
"features": { "features": {
"cart": true, "cart": true,
"search": true, "search": true,
"wishlist": true, "wishlist": false,
"customerAuth": true, "customerAuth": false,
"customCheckout": false "customCheckout": false
} }
} }

View File

@ -10,7 +10,7 @@ import useUpdateItem from '@framework/cart/use-update-item'
import useRemoveItem from '@framework/cart/use-remove-item' import useRemoveItem from '@framework/cart/use-remove-item'
import Quantity from '@components/ui/Quantity' import Quantity from '@components/ui/Quantity'
type ItemOption = { export type ItemOption = {
name: string name: string
nameId: number nameId: number
value: string value: string
@ -93,7 +93,7 @@ const CartItem = ({
width={150} width={150}
height={150} height={150}
src={item.variant.image?.url || placeholderImg} src={item.variant.image?.url || placeholderImg}
alt={item.variant.image?.altText || "Product Image"} alt={item.variant.image?.altText || 'Product Image'}
unoptimized unoptimized
/> />
</a> </a>

View File

@ -57,27 +57,29 @@ const ProductSidebar: FC<ProductSidebarProps> = ({ product, className }) => {
<div className="text-accent-6 pr-1 font-medium text-sm">36 reviews</div> <div className="text-accent-6 pr-1 font-medium text-sm">36 reviews</div>
</div> </div>
<div> <div>
{process.env.COMMERCE_CART_ENABLED && ( <div className="flex flex-row justify-between items-center">
<Button {process.env.COMMERCE_CART_ENABLED && (
aria-label="Add to Cart" <Button
type="button" aria-label="Add to Cart"
className={s.button} type="button"
onClick={addToCart} className={s.button}
loading={loading} onClick={addToCart}
disabled={variant?.availableForSale === false} loading={loading}
> disabled={variant?.availableForSale === false}
{variant?.availableForSale === false >
? 'Not Available' {variant?.availableForSale === false
: 'Add To Cart'} ? 'Not Available'
</Button> : 'Add To Cart'}
)} </Button>
{!variant?.availableForSale && ( )}
<WishlistButton {!variant?.availableForSale && (
className={s.button} <WishlistButton
productId={product.id} className={s.button}
variant={variant!} productId={product.id}
/> variant={variant!}
)} />
)}
</div>
</div> </div>
<div className="mt-6"> <div className="mt-6">
<Collapse title="Care"> <Collapse title="Care">

View File

@ -48,7 +48,11 @@ const WishlistButton: FC<Props> = ({
try { try {
if (itemInWishlist) { if (itemInWishlist) {
await removeItem({ productId, variantId: variant?.id! }) await removeItem({
id: itemInWishlist.productId,
//TODO: enable itemVariantId when using shopify provider
itemVariantId: itemInWishlist.variantId,
})
} else { } else {
await addItem({ await addItem({
productId, productId,

View File

@ -7,7 +7,11 @@ import { Trash } from '@components/icons'
import { Button, Text } from '@components/ui' import { Button, Text } from '@components/ui'
import { useUI } from '@components/ui/context' import { useUI } from '@components/ui/context'
import type { Product } from '@commerce/types/product' import type {
Product,
ProductOption,
ProductVariant,
} from '@commerce/types/product'
import usePrice from '@framework/product/use-price' import usePrice from '@framework/product/use-price'
import useAddItem from '@framework/cart/use-add-item' import useAddItem from '@framework/cart/use-add-item'
import useRemoveItem from '@framework/wishlist/use-remove-item' import useRemoveItem from '@framework/wishlist/use-remove-item'
@ -15,22 +19,24 @@ import type { Wishlist } from '@commerce/types/wishlist'
const placeholderImg = '/product-img-placeholder.svg' const placeholderImg = '/product-img-placeholder.svg'
interface Props { const WishlistCard: React.FC<{
item: Product item: Wishlist
variant: string | number }> = ({ item }) => {
} const product: Product = item.product
const WishlistCard: FC<Props> = ({ item, variant }) => {
const { price } = usePrice({ const { price } = usePrice({
amount: item.price?.value, amount: product.price?.value,
baseAmount: item.price?.retailPrice, baseAmount: product.price?.retailPrice,
currencyCode: item.price?.currencyCode!, currencyCode: product.price?.currencyCode!,
}) })
// @ts-ignore Wishlist is not always enabled // @ts-ignore Wishlist is not always enabled
const removeItem = useRemoveItem({ item }) const removeItem = useRemoveItem({ wishlist: { includeProducts: true } })
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [removing, setRemoving] = useState(false) const [removing, setRemoving] = useState(false)
const { options } = item.product.variants.find(
(variant: ProductVariant) => item.variantId === variant.id
)
// TODO: fix this missing argument issue // TODO: fix this missing argument issue
/* @ts-ignore */ /* @ts-ignore */
const addItem = useAddItem() const addItem = useAddItem()
@ -42,7 +48,11 @@ const WishlistCard: FC<Props> = ({ item, variant }) => {
try { try {
// If this action succeeds then there's no need to do `setRemoving(true)` // If this action succeeds then there's no need to do `setRemoving(true)`
// because the component will be removed from the view // because the component will be removed from the view
await removeItem({ productId: item.id, variantId: variant }) await removeItem({
id: item.productId,
//TODO: enable itemVariantId when using shopify provider
itemVariantId: item.variantId,
})
} catch (error) { } catch (error) {
setRemoving(false) setRemoving(false)
} }
@ -51,8 +61,11 @@ const WishlistCard: FC<Props> = ({ item, variant }) => {
setLoading(true) setLoading(true)
try { try {
await addItem({ await addItem({
productId: String(item.id), //for shopify provider, use the productId and variantId stored in wishlist
variantId: String(item.variants[0].id), productId: item.productId,
variantId: item.variantId,
// productId: String(product.id),
// variantId: String(product.variants[0].id),
}) })
openSidebar() openSidebar()
setLoading(false) setLoading(false)
@ -67,20 +80,46 @@ const WishlistCard: FC<Props> = ({ item, variant }) => {
<Image <Image
width={230} width={230}
height={230} height={230}
src={item.images[0]?.url || placeholderImg} src={product.images[0]?.url || placeholderImg}
alt={item.images[0]?.alt || 'Product Image'} alt={product.images[0]?.alt || 'Product Image'}
/> />
</div> </div>
<div className={s.description}> <div className={s.description}>
<div className="flex-1 mb-6"> <div className="flex-1 mb-6">
<h3 className="text-2xl mb-2 -mt-1"> <h3 className="text-2xl mb-2 -mt-1">
<Link href={`/product${item.path}`}> <Link href={`/product${product.path}`}>
<a>{item.name}</a> <a>{product.name}</a>
</Link> </Link>
</h3> </h3>
{options && options.length > 0 && (
<div className="flex items-center pb-1">
{options.map((option: ProductOption, i: number) => (
<div
key={`${option.id}`}
className="text-sm font-semibold text-accent-7 inline-flex items-center justify-center"
>
{option.displayName}
{option.displayName === 'Color' ? (
<span
className="mx-2 rounded-full bg-transparent border w-5 h-5 p-1 text-accent-9 inline-flex items-center justify-center overflow-hidden"
style={{
backgroundColor: `${option.values[0].label}`,
}}
></span>
) : (
<span className="mx-2 rounded-full bg-transparent border h-5 p-1 text-accent-9 inline-flex items-center justify-center overflow-hidden">
{option.values[0].label}
</span>
)}
{i === options.length - 1 ? '' : <span className="mr-3" />}
</div>
))}
</div>
)}
<div className="mb-4"> <div className="mb-4">
<Text html={item.description} /> <Text html={product.description} />
</div> </div>
</div> </div>
<div> <div>

View File

@ -37,7 +37,7 @@ export async function getStaticProps({
export default function Wishlist() { export default function Wishlist() {
const { data: customer } = useCustomer() const { data: customer } = useCustomer()
// @ts-ignore Shopify - Fix this types // @ts-ignore Shopify - Fix this types
const { data: wishlist, isLoading, isEmpty } = useWishlist() const { data, isLoading, isEmpty } = useWishlist()
return ( return (
<Container className="pt-4"> <Container className="pt-4">
@ -66,14 +66,10 @@ export default function Wishlist() {
</div> </div>
) : ( ) : (
<div className="grid grid-cols-1 gap-6 "> <div className="grid grid-cols-1 gap-6 ">
{wishlist && {data &&
// @ts-ignore - Wishlist Item Type // @ts-ignore - Wishlist Item Type
wishlist.items?.map((item) => ( data.items?.map((item) => (
<WishlistCard <WishlistCard key={item.productId} item={item!} />
key={item.productId}
item={item.product!}
variant={item.variantId}
/>
))} ))}
</div> </div>
)} )}

1182
yarn.lock

File diff suppressed because it is too large Load Diff