Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: quick order preview #2549

Draft
wants to merge 36 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
37d34d4
feat: search product item control
ramondorosario-ct Nov 6, 2024
7255092
feat: rename file
ramondorosario-ct Nov 6, 2024
a3177b0
style: tokens
ramondorosario-ct Nov 6, 2024
28e054a
style: ident
ramondorosario-ct Nov 6, 2024
09f0b28
feat: add skuMatrixControl prop
ramondorosario-ct Nov 6, 2024
5a7bf2b
feat: change timeout time
ramondorosario-ct Nov 6, 2024
e02f24c
feat: search product item control
ramondorosario-ct Nov 6, 2024
2c85f5d
Merge branch 'feat/search-product-item-control' of github.com:cubos-v…
ramondorosario-ct Nov 6, 2024
5e39413
feat: add quantity and onChangeQuantity props
ramondorosario-ct Nov 6, 2024
18c5d5d
chore: file formater
ramondorosario-ct Nov 6, 2024
63a7ef0
feat: quick order settings
ramondorosario-ct Nov 6, 2024
8207add
feat: search product control implementations
ramondorosario-ct Nov 6, 2024
54d5f18
feat: quick order
ramondorosario-ct Nov 6, 2024
035670b
feat: adjust control behavior on mobile version
ramondorosario-ct Nov 8, 2024
f8573ff
feat: adjust control behavior on mobile version
ramondorosario-ct Nov 8, 2024
d5f06a0
Merge branch 'feat/search-product-item-control' into feat/search-prod…
ramondorosario-ct Nov 8, 2024
d774a38
fix: set faststore button in ui
HiagoMoreiraCubos Nov 18, 2024
e63111b
fix: indentation
HiagoMoreiraCubos Nov 19, 2024
ba7344b
fix: indentation on styles.scss
HiagoMoreiraCubos Nov 19, 2024
9d6d4c3
fix: adjust imports
HiagoMoreiraCubos Nov 21, 2024
a6d9d05
feat: apply pr suggestions
ramondorosario-ct Nov 22, 2024
a51fdbb
fix: ident
ramondorosario-ct Nov 22, 2024
a29f5ec
Merge branch 'feat/search-product-item-control' into feat/quick-order
ramondorosario-ct Nov 22, 2024
8f915ea
fix: product name display
ramondorosario-ct Jan 9, 2025
8539301
Merge branch 'feat/search-product-control-implementations' into feat/…
ramondorosario-ct Jan 13, 2025
e0bb7f5
fix: stop propagation click
ramondorosario-ct Jan 13, 2025
31edc17
Merge branch 'feat/search-product-control-implementations' into feat/…
ramondorosario-ct Jan 13, 2025
2568622
Merge branch 'feat/integration-sku-matrix-quick-order' into feat/quic…
ramondorosario-ct Jan 16, 2025
873db87
feat: sku matrix integration
ramondorosario-ct Jan 21, 2025
edcbac2
Merge branch 'feat/search-product-control-implementations' into feat/…
ramondorosario-ct Jan 21, 2025
2e9e4fb
feat: rename method description
ramondorosario-ct Jan 21, 2025
3c3cfdf
Merge branch 'feat/integration-sku-matrix-quick-order' into feat/quic…
ramondorosario-ct Jan 21, 2025
fbe6d27
feat: quick order settings
ramondorosario-ct Jan 24, 2025
c5a0fda
Merge branch 'feat/quick-order-settings' into feat/integration-sku-ma…
ramondorosario-ct Jan 24, 2025
64c5d8d
Merge branch 'feat/quick-order-settings' into feat/quick-order
ramondorosario-ct Jan 24, 2025
8a087a5
Merge branch 'feat/integration-sku-matrix-quick-order' into feat/quic…
ramondorosario-ct Jan 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { forwardRef } from 'react'
import React, { forwardRef, useCallback } from 'react'
import { ProductPrice } from '../..'
import SearchProductItemControl from './SearchProductItemControl'

import type { PriceDefinition } from '../../typings/PriceDefinition'

Expand All @@ -12,23 +13,63 @@ export interface SearchProductItemContentProps {
* Specifies product's prices.
*/
price: PriceDefinition
/**
* Quick order settings.
*/
quickOrder?: {
enabled: boolean
availability: boolean
hasVariants: boolean
skuMatrixControl: React.ReactNode
quantity: number
onChangeQuantity(value: number): void
buyProps?: {
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void
'data-testid': string
'data-sku': string
'data-seller': string
}
}
}

const SearchProductItemContent = forwardRef<
HTMLElement,
SearchProductItemContentProps
>(function SearchProductItemContent({ price, title, ...otherProps }, ref) {
>(function SearchProductItemContent(
{ price, title, quickOrder, ...otherProps },
ref
) {
const renderProductItemContent = useCallback(() => {
return (
<>
<p data-fs-search-product-item-title>{title}</p>
{price.value !== 0 && (
<ProductPrice
data-fs-search-product-item-prices
listPrice={price.listPrice}
value={price.value}
formatter={price.formatter}
/>
)}
</>
)
}, [quickOrder?.enabled])

return (
<section ref={ref} data-fs-search-product-item-content {...otherProps}>
<p data-fs-search-product-item-title>{title}</p>
{!quickOrder?.enabled && renderProductItemContent()}

{price.value !== 0 && (
<ProductPrice
data-fs-search-product-item-prices
listPrice={price.listPrice}
value={price.value}
formatter={price.formatter}
/>
{quickOrder?.enabled && (
<SearchProductItemControl
availability={quickOrder.availability}
hasVariants={quickOrder.hasVariants}
skuMatrixControl={quickOrder.skuMatrixControl}
quantity={quickOrder.quantity}
onChangeQuantity={quickOrder.onChangeQuantity}
{...quickOrder.buyProps}
>
{renderProductItemContent()}
</SearchProductItemControl>
)}
</section>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import React, { forwardRef, HTMLAttributes } from 'react'
import { Badge, Icon, IconButton, Input, Loader, QuantitySelector } from '../..'

import type { ReactNode, MouseEvent } from 'react'

type StatusButtonAddToCartType = 'default' | 'inProgress' | 'completed'

export interface SearchProductItemControlProps
extends Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'onClick'> {
children: ReactNode
/**
* Specifies whether the product is available.
*/
availability: boolean
/**
* Specifies whether the product has variations.
*/
hasVariants: boolean
/**
* Renders the elements of the SKUMatrix.
*/
skuMatrixControl: ReactNode
/**
* Specifies the quantity to be added to the cart.
*/
quantity: number
/**
* Callback that fires when the add to cart button is clicked.
*/
onClick?(e: MouseEvent<HTMLButtonElement>): void
/**
* Callback that fires when the input value changes.
*/
onChangeQuantity(value: number): void
}

const SearchProductItemControl = forwardRef<
HTMLDivElement,
SearchProductItemControlProps
>(function SearchProductItemControl(
{
availability,
children,
hasVariants,
skuMatrixControl,
quantity,
onClick,
onChangeQuantity,
...otherProps
},
ref
) {
const [statusAddToCart, setStatusAddToCart] =
React.useState<StatusButtonAddToCartType>('default')

const showSKUMatrixControl = availability && hasVariants;
const isMobile = window.innerWidth <= 768

function stopPropagationClick(e: MouseEvent) {
e.preventDefault()
e.stopPropagation()
}
function handleAddToCart(event: MouseEvent<HTMLButtonElement>) {
if (onClick) {
setStatusAddToCart('inProgress')

setTimeout(() => {
setStatusAddToCart('completed')
onClick(event)
}, 1000)

setTimeout(() => {
setStatusAddToCart('default')
onChangeQuantity(1)
}, 2000)
}
}

const getIcon = React.useCallback(() => {
switch (statusAddToCart) {
case 'inProgress':
return <Loader />
case 'completed':
return <Icon name="Checked" width={24} height={24} />
default:
return <Icon name="ShoppingCart" width={24} height={24} />
}
}, [statusAddToCart])

return (
<div ref={ref} data-fs-search-product-item-control {...otherProps}>
<div data-fs-search-product-item-control-content>
{!availability && (
<Badge data-fs-search-product-item-control-badge variant="warning">
Out of Stock
</Badge>
)}
{children}
</div>
{availability && !hasVariants && (
<div
data-fs-search-product-item-control-actions
role="group"
onClick={stopPropagationClick}
>
{!isMobile && (
<QuantitySelector
disabled={statusAddToCart !== 'default'}
initial={quantity}
onChange={onChangeQuantity}
/>
)}

{isMobile && (
<Input
data-fs-product-item-control-input
type="number"
min={1}
value={quantity}
onChange={(e) => onChangeQuantity(e.target.valueAsNumber)}
/>
)}

<IconButton
variant="primary"
aria-label="Add product to cart"
onClick={handleAddToCart}
disabled={statusAddToCart === 'inProgress'}
icon={getIcon()}
/>
</div>
)}

{showSKUMatrixControl && (
<div onClick={stopPropagationClick}>{skuMatrixControl}</div>
)}
</div>
)
})
export default SearchProductItemControl
4 changes: 2 additions & 2 deletions packages/core/@generated/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import * as types from './graphql'
* Therefore it is highly recommended to use the babel or swc plugin for production.
*/
const documents = {
'\n fragment ProductSummary_product on StoreProduct {\n id: productID\n slug\n sku\n brand {\n brandName: name\n }\n name\n gtin\n\n isVariantOf {\n productGroupID\n name\n }\n\n image {\n url\n alternateName\n }\n\n brand {\n name\n }\n\n offers {\n lowPrice\n lowPriceWithTaxes\n offers {\n availability\n price\n listPrice\n listPriceWithTaxes\n quantity\n seller {\n identifier\n }\n }\n }\n\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n\n advertisement {\n adId\n adResponseId\n }\n }\n':
'\n fragment ProductSummary_product on StoreProduct {\n id: productID\n slug\n sku\n brand {\n brandName: name\n }\n name\n gtin\n\t\tunitMultiplier\n\n isVariantOf {\n productGroupID\n name\n\t\t\tskuVariants {\n\t\t\t\tallVariantsByName\n\t\t\t\tactiveVariations\n\t\t\t\tslugsMap\n\t\t\t\tavailableVariations\n\t\t\t}\n }\n\n image {\n url\n alternateName\n }\n\n brand {\n name\n }\n\n offers {\n lowPrice\n lowPriceWithTaxes\n offers {\n availability\n price\n listPrice\n listPriceWithTaxes\n\t\t\t\tpriceWithTaxes\n quantity\n seller {\n identifier\n }\n }\n }\n\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n\n advertisement {\n adId\n adResponseId\n }\n }\n':
types.ProductSummary_ProductFragmentDoc,
'\n fragment Filter_facets on StoreFacet {\n ... on StoreFacetRange {\n key\n label\n\n min {\n selected\n absolute\n }\n\n max {\n selected\n absolute\n }\n\n __typename\n }\n ... on StoreFacetBoolean {\n key\n label\n values {\n label\n value\n selected\n quantity\n }\n\n __typename\n }\n }\n':
types.Filter_FacetsFragmentDoc,
Expand Down Expand Up @@ -66,7 +66,7 @@ const documents = {
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function gql(
source: '\n fragment ProductSummary_product on StoreProduct {\n id: productID\n slug\n sku\n brand {\n brandName: name\n }\n name\n gtin\n\n isVariantOf {\n productGroupID\n name\n }\n\n image {\n url\n alternateName\n }\n\n brand {\n name\n }\n\n offers {\n lowPrice\n lowPriceWithTaxes\n offers {\n availability\n price\n listPrice\n listPriceWithTaxes\n quantity\n seller {\n identifier\n }\n }\n }\n\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n\n advertisement {\n adId\n adResponseId\n }\n }\n'
source: '\n fragment ProductSummary_product on StoreProduct {\n id: productID\n slug\n sku\n brand {\n brandName: name\n }\n name\n gtin\n\t\tunitMultiplier\n\n isVariantOf {\n productGroupID\n name\n\t\t\tskuVariants {\n\t\t\t\tallVariantsByName\n\t\t\t\tactiveVariations\n\t\t\t\tslugsMap\n\t\t\t\tavailableVariations\n\t\t\t}\n }\n\n image {\n url\n alternateName\n }\n\n brand {\n name\n }\n\n offers {\n lowPrice\n lowPriceWithTaxes\n offers {\n availability\n price\n listPrice\n listPriceWithTaxes\n\t\t\t\tpriceWithTaxes\n quantity\n seller {\n identifier\n }\n }\n }\n\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n\n advertisement {\n adId\n adResponseId\n }\n }\n'
): typeof import('./graphql').ProductSummary_ProductFragmentDoc
/**
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
Expand Down
51 changes: 46 additions & 5 deletions packages/core/@generated/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1136,9 +1136,19 @@ export type ProductSummary_ProductFragment = {
sku: string
name: string
gtin: string
unitMultiplier: number | null
id: string
brand: { name: string; brandName: string }
isVariantOf: { productGroupID: string; name: string }
isVariantOf: {
productGroupID: string
name: string
skuVariants: {
allVariantsByName: any | null
activeVariations: any | null
slugsMap: any | null
availableVariations: any | null
} | null
}
image: Array<{ url: string; alternateName: string }>
offers: {
lowPrice: number
Expand All @@ -1148,6 +1158,7 @@ export type ProductSummary_ProductFragment = {
price: number
listPrice: number
listPriceWithTaxes: number
priceWithTaxes: number
quantity: number
seller: { identifier: string }
}>
Expand Down Expand Up @@ -1541,9 +1552,19 @@ export type ClientManyProductsQueryQuery = {
sku: string
name: string
gtin: string
unitMultiplier: number | null
id: string
brand: { name: string; brandName: string }
isVariantOf: { productGroupID: string; name: string }
isVariantOf: {
productGroupID: string
name: string
skuVariants: {
allVariantsByName: any | null
activeVariations: any | null
slugsMap: any | null
availableVariations: any | null
} | null
}
image: Array<{ url: string; alternateName: string }>
offers: {
lowPrice: number
Expand All @@ -1553,6 +1574,7 @@ export type ClientManyProductsQueryQuery = {
price: number
listPrice: number
listPriceWithTaxes: number
priceWithTaxes: number
quantity: number
seller: { identifier: string }
}>
Expand Down Expand Up @@ -1674,9 +1696,19 @@ export type ClientSearchSuggestionsQueryQuery = {
sku: string
name: string
gtin: string
unitMultiplier: number | null
id: string
brand: { name: string; brandName: string }
isVariantOf: { productGroupID: string; name: string }
isVariantOf: {
productGroupID: string
name: string
skuVariants: {
allVariantsByName: any | null
activeVariations: any | null
slugsMap: any | null
availableVariations: any | null
} | null
}
image: Array<{ url: string; alternateName: string }>
offers: {
lowPrice: number
Expand All @@ -1686,6 +1718,7 @@ export type ClientSearchSuggestionsQueryQuery = {
price: number
listPrice: number
listPriceWithTaxes: number
priceWithTaxes: number
quantity: number
seller: { identifier: string }
}>
Expand Down Expand Up @@ -1801,9 +1834,16 @@ export const ProductSummary_ProductFragmentDoc = new TypedDocumentString(
}
name
gtin
unitMultiplier
isVariantOf {
productGroupID
name
skuVariants {
allVariantsByName
activeVariations
slugsMap
availableVariations
}
}
image {
url
Expand All @@ -1820,6 +1860,7 @@ export const ProductSummary_ProductFragmentDoc = new TypedDocumentString(
price
listPrice
listPriceWithTaxes
priceWithTaxes
quantity
seller {
identifier
Expand Down Expand Up @@ -2262,7 +2303,7 @@ export const ClientAllVariantProductsQueryDocument = {
export const ClientManyProductsQueryDocument = {
__meta__: {
operationName: 'ClientManyProductsQuery',
operationHash: '14148671fbf53498fad5c600ee87765920145019',
operationHash: '1adc93c70f16173540c50f725ee09a2d67cb85ab',
},
} as unknown as TypedDocumentString<
ClientManyProductsQueryQuery,
Expand All @@ -2289,7 +2330,7 @@ export const ClientProductQueryDocument = {
export const ClientSearchSuggestionsQueryDocument = {
__meta__: {
operationName: 'ClientSearchSuggestionsQuery',
operationHash: '47e48eaee91d16a4237eb2c1241bc2ed3e2ad9bb',
operationHash: 'b548281d477a173be7b6960434604d69769a97e7',
},
} as unknown as TypedDocumentString<
ClientSearchSuggestionsQueryQuery,
Expand Down
Loading
Loading