Skip to content

Commit 2119bcb

Browse files
mocca102Jesse Warrensblaurock
authored
Create recommendations (#7)
* Create recommendations * add recommendations * fix modal styles * add responsive styles * Minor visual tweaks. * Change inset, don't show at small resolutions. * Disable carousel scrolling. Co-authored-by: Jesse Warren <jessewarren@Jesses-MacBook-Pro.local> Co-authored-by: Steve Blaurock <sblaurock@gmail.com>
1 parent a8a4c18 commit 2119bcb

File tree

11 files changed

+322
-3
lines changed

11 files changed

+322
-3
lines changed

package-lock.json

Lines changed: 80 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
"downshift": "^6.1.7",
1818
"react": "^18.2.0",
1919
"react-dom": "^18.2.0",
20+
"react-modal": "^3.16.1",
21+
"react-multi-carousel": "^2.8.2",
2022
"react-router-dom": "^6.3.0",
2123
"react-scripts": "5.0.1"
2224
},

src/components/Browse/index.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ function Browse() {
1414
const location = useLocation();
1515
const browseGroup = location.state;
1616

17-
const [loadStatus, setLoadStatus] = useState(loadStatuses.SUCCESS);
17+
const [loadStatus, setLoadStatus] = useState(loadStatuses.STALE);
1818
const [items, setItems] = useState([]);
1919
const [totalResults, setTotalResults] = useState(0);
2020
const [error, setError] = useState();

src/components/ProductCard.jsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22

3-
function ProductCard({ product }) {
3+
function ProductCard({ product, onProductClick }) {
44
const imageTagClassesLoading = 'w-[225px] md:w-[300px] transition-opacity opacity-0 ml-auto mr-auto';
55
const imageTagClassesLoaded = 'w-[225px] md:w-[300px] transition-opacity opacity-100 ml-auto mr-auto';
66

@@ -10,6 +10,10 @@ function ProductCard({ product }) {
1010
data-cnstrc-item-id={ product.data.id }
1111
data-cnstrc-item-name={ product.value }
1212
data-cnstrc-item-variation-id={ product.data?.variation_id }
13+
onClick={ () => onProductClick(product) }
14+
onKeyDown={ (e) => e.key === 'Enter' && onProductClick(product) }
15+
role="button"
16+
tabIndex={ 0 }
1317
>
1418
<div className="mb-1 h-[225px]">
1519
<img
@@ -22,6 +26,7 @@ function ProductCard({ product }) {
2226
</div>
2327
{product.value}
2428
</div>
29+
2530
);
2631
}
2732

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import React from 'react';
2+
3+
function RecommendationCard({ product }) {
4+
const imageTagClassesLoading = 'h-[100px] md:h-[150px] transition-opacity opacity-0 ml-auto mr-auto';
5+
const imageTagClassesLoaded = 'h-[100px] md:h-[150px] transition-opacity opacity-100 ml-auto mr-auto';
6+
7+
return (
8+
<div
9+
role="button"
10+
tabIndex={ 0 }
11+
className="recommendation text-center flex flex-col items-center"
12+
data-cnstrc-item="Recommendation"
13+
data-cnstrc-item-id={ product.data.id }
14+
data-cnstrc-item-name={ product.value }
15+
data-cnstrc-item-variation-id={ product.data?.variation_id }
16+
data-cnstrc-strategy-id={ product.strategy?.id }
17+
>
18+
<div className="mb-3">
19+
<img
20+
className={ imageTagClassesLoading }
21+
src={ product.data.image_url }
22+
alt={ product.value }
23+
onError={ (event) => { event.target.style.display = 'none'; } }
24+
onLoad={ (event) => { event.target.className = imageTagClassesLoaded; } }
25+
/>
26+
</div>
27+
<div className="w-2/3 text-center">
28+
{product.value}
29+
</div>
30+
</div>
31+
);
32+
}
33+
34+
export default RecommendationCard;
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/* eslint-disable react/jsx-props-no-spreading */
2+
import * as React from 'react';
3+
import Carousel from 'react-multi-carousel';
4+
import RecommendationCard from './RecommendationCard';
5+
import 'react-multi-carousel/lib/styles.css';
6+
7+
const responsive = {
8+
desktop: {
9+
breakpoint: {
10+
max: 3000,
11+
min: 1024,
12+
},
13+
items: 4,
14+
partialVisibilityGutter: 40,
15+
},
16+
tablet: {
17+
breakpoint: {
18+
max: 1024,
19+
min: 768,
20+
},
21+
items: 3,
22+
partialVisibilityGutter: 30,
23+
},
24+
mobile: {
25+
breakpoint: {
26+
max: 768,
27+
min: 500,
28+
},
29+
items: 2,
30+
partialVisibilityGutter: 30,
31+
},
32+
};
33+
34+
function RecommendationsResults(props) {
35+
const {
36+
items,
37+
dataAttributes,
38+
} = props;
39+
return (
40+
<div
41+
id="recommendations"
42+
className="recommendations-carousel"
43+
data-cnstrc-recommendations
44+
data-cnstrc-recommendations-pod-id={ dataAttributes.dataCnstrcPodId }
45+
data-cnstrc-num-results={ dataAttributes.dataCnstrcNumResults }
46+
data-cnstrc-result-id={ dataAttributes.dataCnstrcResultId }
47+
>
48+
<Carousel
49+
responsive={ responsive }
50+
additionalTransfrom={ 0 }
51+
containerClass="container"
52+
itemClass=""
53+
sliderClass=""
54+
slidesToSlide={ 0 }
55+
centerMode
56+
removeArrowOnDeviceType={ ['tablet', 'mobile', 'desktop'] }
57+
draggable={ false }
58+
{ ...dataAttributes }
59+
>
60+
{ items.map((item) => <RecommendationCard key={ item.data.id } product={ item } />) }
61+
</Carousel>
62+
</div>
63+
64+
);
65+
}
66+
67+
export default RecommendationsResults;
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import * as React from 'react';
2+
import { useEffect, useState } from 'react';
3+
import { fetchRecommendations } from '../../utils';
4+
import { loadStatuses } from '../../utils/constants';
5+
import RecommendationsResults from './RecommendationsResults';
6+
7+
function Recommendations() {
8+
const [loaded, setLoaded] = useState(loadStatuses.STALE);
9+
const [recommendations, setRecommendations] = useState([]);
10+
const [podData, setPodData] = useState();
11+
const [resultId, setResultId] = useState();
12+
const [numResults, setNumResults] = useState();
13+
14+
useEffect(() => {
15+
const fetchRecommendationsFromAPI = async () => {
16+
setLoaded(loadStatuses.LOADING);
17+
try {
18+
const response = await fetchRecommendations('item_detail');
19+
20+
setRecommendations(response?.response?.results);
21+
setNumResults(response?.response?.total_num_results);
22+
setResultId(response?.result_id);
23+
setPodData({
24+
podName: response?.response?.pod?.display_name,
25+
poId: response?.response?.pod?.id,
26+
});
27+
setLoaded(loadStatuses.SUCCESS);
28+
} catch (e) {
29+
setLoaded(loadStatuses.FAILED);
30+
}
31+
};
32+
fetchRecommendationsFromAPI();
33+
}, []);
34+
35+
return (
36+
<div className="m-10 border-t-2 border-gray-200">
37+
<div className="text-lg mb-5 text-center mt-10 italic">{podData?.podName}</div>
38+
{ loaded === loadStatuses.SUCCESS && (
39+
<RecommendationsResults
40+
items={ recommendations }
41+
dataAttributes={ {
42+
dataCnstrcPodId: podData?.poId,
43+
dataCnstrcNumResults: numResults,
44+
dataCnstrcResultId: resultId,
45+
} }
46+
/>
47+
)}
48+
</div>
49+
);
50+
}
51+
52+
export default Recommendations;

0 commit comments

Comments
 (0)