Skip to content

Commit 3e57edc

Browse files
authored
Merge pull request #5 from imranhsayed/feature/display-products
Display products
2 parents 88eff82 + 8ee73a6 commit 3e57edc

File tree

11 files changed

+189
-30
lines changed

11 files changed

+189
-30
lines changed

.env-example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
#Backend WordPress Site URL.
12
NEXT_PUBLIC_WORDPRESS_SITE_URL=https://example.com
23

4+
#Frontend Next.js Site URL
5+
NEXT_PUBLIC_SITE_URL=http://localhost:3000
6+
37
WC_CONSUMER_KEY=ck_xx
48
WC_CONSUMER_SECRET=cs_xx

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,9 @@ npm run dev
5151

5252
## Configuration :wrench:
5353

54-
1. (Required) Create a `.env` file taking reference from `.env-example` and update your WordPressSite URL.
54+
1. (Required) Create a `.env` file taking reference from `.env-example` and update your WordPressSite URL and Frontend next.js URL.
5555
- `NEXT_PUBLIC_WORDPRESS_URL=https://example.com`
56+
- `NEXT_PUBLIC_SITE_URL=http://localhost.com` ( This will be your frontend Next.js URL)
5657

5758
2. Add your `WC_CONSUMER_KEY` and `WC_CONSUMER_SECRET` to the `.env` by following [WooCommerce > Settings > Advanced > REST API](https://woocommerce.github.io/woocommerce-rest-api-docs/#authentication)
5859

next.config.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
const path = require('path');
2+
const allowedImageWordPressDomain = new URL( process.env.NEXT_PUBLIC_WORDPRESS_SITE_URL ).hostname;
3+
4+
module.exports = {
5+
trailingSlash: false,
6+
webpackDevMiddleware: config => {
7+
config.watchOptions = {
8+
poll: 1000,
9+
aggregateTimeout: 300
10+
}
11+
12+
return config
13+
},
14+
sassOptions: {
15+
includePaths: [path.join(__dirname, 'styles')]
16+
},
17+
/**
18+
* We specify which domains are allowed to be optimized.
19+
* This is needed to ensure that external urls can't be abused.
20+
* @see https://nextjs.org/docs/basic-features/image-optimization#domains
21+
*/
22+
images: {
23+
domains: [ allowedImageWordPressDomain, 'via.placeholder.com' ],
24+
},
25+
}

nextjs.config.js

Lines changed: 0 additions & 15 deletions
This file was deleted.

package-lock.json

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

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"version": "0.1.0",
44
"private": true,
55
"scripts": {
6-
"dev": "next dev",
6+
"dev": "NODE_OPTIONS='--inspect' next dev",
77
"build": "next build",
88
"start": "next start",
99
"svg": "svgr -d src/components/icons src/components/icons/svgs"
@@ -12,9 +12,11 @@
1212
"@svgr/cli": "^5.5.0",
1313
"@woocommerce/woocommerce-rest-api": "^1.0.1",
1414
"axios": "^0.21.1",
15+
"classnames": "^2.3.1",
1516
"dompurify": "^2.3.1",
1617
"lodash": "^4.17.21",
1718
"next": "11.1.0",
19+
"prop-types": "^15.7.2",
1820
"react": "17.0.2",
1921
"react-dom": "17.0.2",
2022
"sass": "^1.38.0"

pages/index.js

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,23 @@
33
*/
44
import Header from '../src/components/layouts/header';
55
import Footer from '../src/components/layouts/footer';
6-
import { HEADER_FOOTER_ENDPOINT } from '../src/utils/constants/endpoints';
6+
import Products from '../src/components/products';
7+
import { GET_PRODUCTS_ENDPOINT, HEADER_FOOTER_ENDPOINT } from '../src/utils/constants/endpoints';
78

89
/**
910
* External Dependencies.
1011
*/
1112
import axios from 'axios';
1213

13-
export default function Home({data}) {
14-
const { header, footer } = data;
14+
export default function Home({ headerFooter, products }) {
15+
16+
const { header, footer } = headerFooter || {};
1517

1618
return (
1719
<div >
1820
<Header header={header}/>
19-
<main >
20-
<h1 >
21-
Welcome to <a href="https://nextjs.org">Next.js!</a>
22-
</h1>
23-
<p className="text-green-500">Hello</p>
21+
<main className="container mx-auto p-4">
22+
<Products products={products}/>
2423
</main>
2524

2625
<Footer footer={footer}/>
@@ -29,10 +28,15 @@ export default function Home({data}) {
2928
}
3029

3130
export async function getStaticProps() {
32-
const { data } = await axios.get( HEADER_FOOTER_ENDPOINT );
31+
32+
const { data: headerFooterData } = await axios.get( HEADER_FOOTER_ENDPOINT );
33+
const { data: productsData } = await axios.get( GET_PRODUCTS_ENDPOINT );
3334

3435
return {
35-
props: data || {},
36+
props: {
37+
headerFooter: headerFooterData?.data ?? {},
38+
products: productsData?.products ?? {}
39+
},
3640

3741
/**
3842
* Revalidate means that if a new request comes to server, then every 1 sec it will check

src/components/image/index.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import Img from 'next/image';
2+
3+
import PropTypes from 'prop-types';
4+
import cx from 'classnames';
5+
import {DEFAULT_IMG_URL} from '../../utils/constants/images';
6+
7+
/**
8+
* Image Component.
9+
* We don't need to add srcSet, as Next js will generate that.
10+
* @see https://nextjs.org/docs/api-reference/next/image#other-props
11+
* @see https://nextjs.org/docs/basic-features/image-optimization#device-sizes
12+
*
13+
* @param {Object} props Component props.
14+
*
15+
* @return {jsx}
16+
*/
17+
const Image = ( props ) => {
18+
const {altText, title, width, height, sourceUrl, className, layout, objectFit, containerClassNames, showDefault, ...rest} = props;
19+
20+
if ( ! sourceUrl && ! showDefault ) {
21+
return null;
22+
}
23+
24+
/**
25+
* If we use layout = fill then, width and height of the image cannot be used.
26+
* and the image fills on the entire width and the height of its parent container.
27+
* That's we need to wrap our image in a container and give it a height and width.
28+
* Notice that in this case, the given height and width is being used for container and not img.
29+
*/
30+
if ( 'fill' === layout ) {
31+
const attributes = {
32+
alt: altText || title,
33+
src: sourceUrl || ( showDefault ? DEFAULT_IMG_URL : '' ),
34+
layout: 'fill',
35+
className: cx( 'object-cover', className ),
36+
...rest
37+
};
38+
39+
return (
40+
<div className={cx( 'relative', containerClassNames ) }>
41+
<Img {...attributes}/>
42+
</div>
43+
);
44+
} else {
45+
const attributes = {
46+
alt: altText || title,
47+
src: sourceUrl || ( showDefault ? DEFAULT_IMG_URL : '' ),
48+
width: width || 'auto',
49+
height: height || 'auto',
50+
className,
51+
...rest
52+
};
53+
return <Img {...attributes} />;
54+
}
55+
};
56+
57+
Image.propTypes = {
58+
altText: PropTypes.string,
59+
title: PropTypes.string,
60+
sourceUrl: PropTypes.string,
61+
layout: PropTypes.string,
62+
showDefault: PropTypes.bool,
63+
containerClassName: PropTypes.string,
64+
className: PropTypes.string
65+
};
66+
67+
Image.defaultProps = {
68+
altText: '',
69+
title: '',
70+
sourceUrl: '',
71+
showDefault: true,
72+
containerClassNames: '',
73+
className: 'product__image',
74+
};
75+
76+
export default Image;

src/components/products/index.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { isArray, isEmpty } from 'lodash';
2+
import Link from 'next/link';
3+
import Image from '../image';
4+
5+
const Products = ({ products }) => {
6+
7+
if ( isEmpty( products ) || !isArray( products ) ) {
8+
return null;
9+
}
10+
11+
return (
12+
<div className="flex flex-wrap -mx-2 overflow-hidden">
13+
14+
{ products.length ? products.map( product => {
15+
const img = product?.images?.[0] ?? {};
16+
return (
17+
<div key={ product?.id } className="my-2 px-2 w-full overflow-hidden sm:w-1/2 md:w-1/3 xl:w-1/4">
18+
<Link href={product?.permalink ?? '/'}>
19+
<a>
20+
<Image
21+
sourceUrl={ img?.src ?? '' }
22+
altText={ img?.alt ?? ''}
23+
title={ product?.name ?? '' }
24+
width="380"
25+
height="380"
26+
/>
27+
<h3 className="font-bold uppercase">{ product?.name ?? '' }</h3>
28+
</a>
29+
</Link>
30+
</div>
31+
)
32+
} ) : null }
33+
34+
</div>
35+
)
36+
}
37+
38+
export default Products;

src/utils/constants/endpoints.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export const HEADER_FOOTER_ENDPOINT = `${ process.env.NEXT_PUBLIC_WORDPRESS_SITE_URL }/wp-json/rae/v1/header-footer?header_location_id=hcms-menu-header&footer_location_id=hcms-menu-footer`;
2+
export const GET_PRODUCTS_ENDPOINT = `${process.env.NEXT_PUBLIC_SITE_URL}/api/get-products`;

src/utils/constants/images.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const DEFAULT_IMG_URL = 'https://via.placeholder.com/380x380';

0 commit comments

Comments
 (0)