mirror of
https://github.com/vercel/commerce.git
synced 2025-06-19 05:31:22 +00:00
Sell only available products / fix out of stock
This commit is contained in:
parent
81ac0dd1d4
commit
c2e5eb8501
@ -146,8 +146,12 @@ const ProductView: FC<Props> = ({ product }) => {
|
|||||||
className={s.button}
|
className={s.button}
|
||||||
onClick={addToCart}
|
onClick={addToCart}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
|
disabled={variant?.availableForSale === false}
|
||||||
>
|
>
|
||||||
Add to Cart
|
{variant?.isInStock === false &&
|
||||||
|
variant?.availableForSale === false
|
||||||
|
? 'Out Of Stock'
|
||||||
|
: 'Add To Cart'}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -190,6 +190,12 @@ interface ProductImage {
|
|||||||
interface ProductVariant2 {
|
interface ProductVariant2 {
|
||||||
id: string | number
|
id: string | number
|
||||||
options: ProductOption[]
|
options: ProductOption[]
|
||||||
|
// Indicates whether this product variant is in stock.
|
||||||
|
isInStock?: boolean
|
||||||
|
// Indicates if the product variant is available for sale.
|
||||||
|
availableForSale?: boolean
|
||||||
|
// The total sellable quantity of the variant for online sales channels.
|
||||||
|
quantityAvailable?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ProductPrice {
|
interface ProductPrice {
|
||||||
|
@ -10,11 +10,14 @@ export const handler: SWRHook<Customer | null> = {
|
|||||||
query: getCustomerQuery,
|
query: getCustomerQuery,
|
||||||
},
|
},
|
||||||
async fetcher({ options, fetch }) {
|
async fetcher({ options, fetch }) {
|
||||||
|
const customerAccessToken = getCustomerToken()
|
||||||
|
if (customerAccessToken) {
|
||||||
const data = await fetch<any | null>({
|
const data = await fetch<any | null>({
|
||||||
...options,
|
...options,
|
||||||
variables: { customerAccessToken: getCustomerToken() },
|
variables: { customerAccessToken },
|
||||||
})
|
})
|
||||||
return data.customer ?? null
|
return data.customer ?? null
|
||||||
|
}
|
||||||
},
|
},
|
||||||
useHook: ({ useData }) => (input) => {
|
useHook: ({ useData }) => (input) => {
|
||||||
return useData({
|
return useData({
|
||||||
|
@ -18,9 +18,7 @@ export interface LineItem extends Core.LineItem {
|
|||||||
* Cart mutations
|
* Cart mutations
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export type Product = Core.Product & {
|
export type Product = Core.Product
|
||||||
totalInventory?: Maybe<Scalars['Int']>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type OptionSelections = {
|
export type OptionSelections = {
|
||||||
option_id: number
|
option_id: number
|
||||||
|
@ -7,14 +7,14 @@ export const getSearchVariables = ({
|
|||||||
categoryId,
|
categoryId,
|
||||||
sort,
|
sort,
|
||||||
}: SearchProductsInput) => {
|
}: SearchProductsInput) => {
|
||||||
let query = ''
|
let query = 'available_for_sale:true'
|
||||||
|
|
||||||
if (search) {
|
if (search) {
|
||||||
query += `product_type:${search} OR title:${search} OR tag:${search}`
|
query += ` product_type:${search} OR title:${search} OR tag:${search}`
|
||||||
}
|
}
|
||||||
|
|
||||||
if (brandId) {
|
if (brandId) {
|
||||||
query += `${search ? ' AND ' : ''}vendor:${brandId}`
|
query += ` ${search ? 'AND ' : ''}vendor:${brandId}`
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -51,15 +51,27 @@ const normalizeProductImages = ({ edges }: ImageConnection) =>
|
|||||||
const normalizeProductVariants = ({ edges }: ProductVariantConnection) => {
|
const normalizeProductVariants = ({ edges }: ProductVariantConnection) => {
|
||||||
return edges?.map(
|
return edges?.map(
|
||||||
({
|
({
|
||||||
node: { id, selectedOptions, sku, title, priceV2, compareAtPriceV2 },
|
node: {
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
sku,
|
||||||
|
priceV2,
|
||||||
|
compareAtPriceV2,
|
||||||
|
selectedOptions,
|
||||||
|
requiresShipping,
|
||||||
|
availableForSale,
|
||||||
|
quantityAvailable,
|
||||||
|
},
|
||||||
}) => {
|
}) => {
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
name: title,
|
name: title,
|
||||||
sku: sku ?? id,
|
sku: sku ?? '',
|
||||||
price: +priceV2.amount,
|
price: +priceV2.amount,
|
||||||
listPrice: +compareAtPriceV2?.amount,
|
listPrice: +compareAtPriceV2?.amount,
|
||||||
requiresShipping: true,
|
requiresShipping,
|
||||||
|
availableForSale,
|
||||||
|
isInStock: Boolean(Number(quantityAvailable) > 0),
|
||||||
options: selectedOptions.map(({ name, value }: SelectedOption) => {
|
options: selectedOptions.map(({ name, value }: SelectedOption) => {
|
||||||
const options = normalizeProductOption({
|
const options = normalizeProductOption({
|
||||||
id,
|
id,
|
||||||
@ -84,7 +96,6 @@ export function normalizeProduct(productNode: ShopifyProduct): Product {
|
|||||||
handle,
|
handle,
|
||||||
priceRange,
|
priceRange,
|
||||||
options,
|
options,
|
||||||
totalInventory,
|
|
||||||
...rest
|
...rest
|
||||||
} = productNode
|
} = productNode
|
||||||
|
|
||||||
@ -98,7 +109,6 @@ export function normalizeProduct(productNode: ShopifyProduct): Product {
|
|||||||
price: money(priceRange?.minVariantPrice),
|
price: money(priceRange?.minVariantPrice),
|
||||||
images: normalizeProductImages(images),
|
images: normalizeProductImages(images),
|
||||||
variants: variants ? normalizeProductVariants(variants) : [],
|
variants: variants ? normalizeProductVariants(variants) : [],
|
||||||
totalInventory,
|
|
||||||
options: options
|
options: options
|
||||||
? options
|
? options
|
||||||
.filter((o) => o.name !== 'Title') // By default Shopify adds a 'Title' name when there's only one option. We don't need it. https://community.shopify.com/c/Shopify-APIs-SDKs/Adding-new-product-variant-is-automatically-adding-quot-Default/td-p/358095
|
.filter((o) => o.name !== 'Title') // By default Shopify adds a 'Title' name when there's only one option. We don't need it. https://community.shopify.com/c/Shopify-APIs-SDKs/Adding-new-product-variant-is-automatically-adding-quot-Default/td-p/358095
|
||||||
|
@ -10,7 +10,7 @@ edges {
|
|||||||
vendor
|
vendor
|
||||||
handle
|
handle
|
||||||
description
|
description
|
||||||
totalInventory
|
availableForSale
|
||||||
priceRange {
|
priceRange {
|
||||||
minVariantPrice {
|
minVariantPrice {
|
||||||
amount
|
amount
|
||||||
@ -48,7 +48,7 @@ products(
|
|||||||
const getAllProductsQuery = /* GraphQL */ `
|
const getAllProductsQuery = /* GraphQL */ `
|
||||||
query getAllProducts(
|
query getAllProducts(
|
||||||
$first: Int = 250
|
$first: Int = 250
|
||||||
$query: String = ""
|
$query: String = "available_for_sale:true"
|
||||||
$sortKey: ProductSortKeys = RELEVANCE
|
$sortKey: ProductSortKeys = RELEVANCE
|
||||||
$reverse: Boolean = false
|
$reverse: Boolean = false
|
||||||
) {
|
) {
|
||||||
|
@ -8,7 +8,6 @@ const getProductQuery = /* GraphQL */ `
|
|||||||
vendor
|
vendor
|
||||||
description
|
description
|
||||||
descriptionHtml
|
descriptionHtml
|
||||||
totalInventory
|
|
||||||
options {
|
options {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
@ -34,6 +33,9 @@ const getProductQuery = /* GraphQL */ `
|
|||||||
id
|
id
|
||||||
title
|
title
|
||||||
sku
|
sku
|
||||||
|
availableForSale
|
||||||
|
requiresShipping
|
||||||
|
quantityAvailable
|
||||||
selectedOptions {
|
selectedOptions {
|
||||||
name
|
name
|
||||||
value
|
value
|
||||||
|
Loading…
x
Reference in New Issue
Block a user