Skip to content

Commit 5892750

Browse files
SKU selection (#41)
* Added SKU selection to product page * Fixed issue on products without skus * Added filter for child products
1 parent 9dd9e4d commit 5892750

File tree

8 files changed

+190
-9
lines changed

8 files changed

+190
-9
lines changed

src/Category.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ export const Category: React.FC = () => {
6666
<h1 className="category__categoryname">{category?.name ?? ' '}</h1>
6767

6868
<ul className="category__productlist">
69-
{products && products.data.map(product => (
69+
{products && products.data
70+
.filter(product => !product.relationships.hasOwnProperty('parent'))
71+
.map(product => (
7072
<li key={product.id} className="category__product">
7173
<ProductThumbnail product={product} />
7274
</li>

src/Product.tsx

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState } from 'react';
1+
import React, { useEffect, useState } from 'react';
22
import { useParams } from 'react-router-dom';
33
import { useResolve, useProductImages } from './hooks';
44
import { loadProductBySlug } from './service';
@@ -7,6 +7,7 @@ import { SocialShare } from './SocialShare';
77
import { useTranslation, useCurrency } from './app-state';
88
import { isProductAvailable } from './helper';
99
import { Availability } from './Availability';
10+
import { VariationsSelector } from './VariationsSelector';
1011

1112
import './Product.scss';
1213

@@ -25,6 +26,11 @@ export const Product: React.FC = () => {
2526
async () => loadProductBySlug(productSlug, selectedLanguage, selectedCurrency),
2627
[productSlug, selectedLanguage, selectedCurrency]
2728
);
29+
const [productId, setProductId] = useState('');
30+
31+
useEffect(() => {
32+
product && setProductId(product.id);
33+
}, [product])
2834

2935
const productImageHrefs = useProductImages(product);
3036
const [currentImageIndex, setCurrentImageIndex] = useState(0);
@@ -40,6 +46,10 @@ export const Product: React.FC = () => {
4046
setCurrentImageIndex(currentImageIndex + 1);
4147
};
4248

49+
function handleVariationChange(childID: string) {
50+
setProductId(childID);
51+
}
52+
4353
return (
4454
<div className="product">
4555
{product ? (
@@ -72,11 +82,20 @@ export const Product: React.FC = () => {
7282
<div className="product__comparecheck">
7383
<CompareCheck product={product} />
7484
</div>
85+
{
86+
product.meta.variations
87+
? <VariationsSelector product={product} onChange={handleVariationChange} />
88+
: ''
89+
}
7590
<div className="product__moltinbtncontainer">
76-
<span
77-
className="moltin-buy-button"
78-
data-moltin-product-id={product.id}
79-
></span>
91+
{
92+
productId &&
93+
<span
94+
className="moltin-buy-button"
95+
data-moltin-product-id={productId}
96+
key={productId}
97+
></span>
98+
}
8099
</div>
81100
<div className="product__description">
82101
{product.description}

src/VariationOptions.scss

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
@import "./theme/common";
2+
3+
.variationoptions {
4+
&__input {
5+
display: none;
6+
}
7+
8+
&__button {
9+
display: inline-block;
10+
margin: 0 10px 12px 0;
11+
}
12+
13+
&__label {
14+
color: $mainTextColor;
15+
padding: 10px 15px;
16+
box-sizing: border-box;
17+
position: relative;
18+
display: inline-block;
19+
border: solid 1px $mainBorderColor;
20+
background-color: $mainBackgroundColor;
21+
font-size: 14px;
22+
text-align: center;
23+
cursor: pointer;
24+
}
25+
26+
&__input:checked + &__label {
27+
color: $firstComplimentColor;
28+
border-color: $firstComplimentColor;
29+
font-weight: bold;
30+
}
31+
}

src/VariationOptions.tsx

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import React, { useState } from 'react';
2+
3+
import './VariationOptions.scss';
4+
5+
6+
interface Option {
7+
id: string,
8+
name: string,
9+
}
10+
11+
interface Variation {
12+
options: Option[]
13+
}
14+
15+
interface VariationOptionsParams {
16+
variation: Variation,
17+
defaultOption: boolean,
18+
onChange: (option: Option, variation: Variation) => void,
19+
}
20+
21+
export const VariationOptions: React.FC<VariationOptionsParams> = ({ variation, defaultOption, onChange }) => {
22+
23+
const [checkedOption, setCheckedOption] = useState(defaultOption);
24+
const handleChange = (option: any) => {
25+
setCheckedOption(option.id);
26+
onChange(option, variation);
27+
};
28+
29+
return (
30+
<div className="variationoptions">
31+
{variation.options.map((option: any) => (
32+
<div className="variationoptions__button" key={option.id}>
33+
<input
34+
className="variationoptions__input"
35+
type="radio"
36+
id={option.id}
37+
name={option.name}
38+
onChange={() => handleChange(option)}
39+
value={option.id}
40+
checked={checkedOption === option.id}
41+
/>
42+
<label
43+
className="variationoptions__label"
44+
htmlFor={option.id}
45+
>
46+
{option.name}
47+
</label>
48+
</div>
49+
))}
50+
</div>
51+
);
52+
};

src/VariationsSelector.scss

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
@import "./theme/common";
2+
3+
.variationsselector {
4+
&__title {
5+
color: $mainTextColor;
6+
font-weight: 500;
7+
text-transform: uppercase;
8+
font-size: 12px;
9+
padding: 10px 0;
10+
}
11+
}

src/VariationsSelector.tsx

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import React, { useState } from 'react';
2+
import { VariationOptions } from './VariationOptions';
3+
4+
import './VariationsSelector.scss';
5+
6+
7+
interface VariationsSelectorParams {
8+
product: any
9+
onChange: (childID: string) => any
10+
}
11+
12+
export const VariationsSelector: React.FC<VariationsSelectorParams> = ({ product, onChange }) => {
13+
const { variations, variation_matrix: variationMatrix } = product.meta;
14+
15+
const initialOptions = variations.reduce(
16+
(acum: any, variation: any) => ({ ...acum, [variation.id]: variation.options[0].id }),
17+
{}
18+
);
19+
20+
const [selectedOptions, setSelectedOptions] = useState(initialOptions);
21+
22+
const getChildID = (options: any, prodMatrix: any): any => {
23+
if (options.length !== 0 && typeof prodMatrix === 'string') {
24+
return prodMatrix
25+
}
26+
27+
for (const x in options) {
28+
if (options.hasOwnProperty(x) && prodMatrix[options[x]]) {
29+
const subMatrix = prodMatrix[options[x]];
30+
return getChildID(options, subMatrix);
31+
}
32+
}
33+
return null;
34+
};
35+
36+
const processOptions = (updatedOptions: any) => {
37+
const options = Object.values(updatedOptions);
38+
const childID = getChildID(options, variationMatrix);
39+
onChange(childID);
40+
};
41+
42+
const handleChange = (option: any, variation: any): any => {
43+
const updatedOptions = { ...selectedOptions, [variation.id]: option.id };
44+
setSelectedOptions(updatedOptions);
45+
processOptions(updatedOptions);
46+
};
47+
48+
return (
49+
<div className="variationsselector">
50+
{variations.map((variation: any) => (
51+
<div key={variation.id}>
52+
<div className="variationsselector__title">{variation?.name}:</div>
53+
<VariationOptions
54+
variation={variation}
55+
key={variation.id}
56+
defaultOption={selectedOptions[variation.id]}
57+
onChange={handleChange}
58+
/>
59+
</div>
60+
))}
61+
</div>
62+
);
63+
};

src/routes.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ export const routes: RouteConfig[] = [
3636
export const routesAccount: RouteConfig[] = [
3737
{ exact: true, path: '/account', component: Profile, },
3838
{ exact: true, path: '/account/address', component: Address, },
39-
{ exact: true, path: '/search', component: Search, }
4039
];
4140

4241
export function createHomeUrl(): string {

src/service.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export interface ProductBase {
5555
without_tax: FormattedPrice;
5656
};
5757
variations?: any[];
58+
variation_matrix?: any;
5859
};
5960
relationships: {
6061
main_image: {
@@ -242,7 +243,7 @@ export async function loadProductBySlug(productSlug: string, language: string, c
242243

243244
const moltin = MoltinGateway({ client_id: config.clientId, currency: currency });
244245

245-
const result = await moltin.Products
246+
const resultSlug = await moltin.Products
246247
.Limit(1)
247248
.Filter({
248249
eq: {
@@ -251,7 +252,10 @@ export async function loadProductBySlug(productSlug: string, language: string, c
251252
})
252253
.All();
253254

254-
const product = result.data[0];
255+
const productId = resultSlug?.data[0]?.id;
256+
const result = await moltin.Products.Get(productId);
257+
const product = result.data;
258+
255259
setProductCache(product.slug, language, currency, product);
256260

257261
return product;

0 commit comments

Comments
 (0)