Skip to content

Commit 09ca0c6

Browse files
authored
Merge pull request #41 from imranhsayed/feature/blogs
Blog Page
2 parents aaae98c + 69f45cb commit 09ca0c6

26 files changed

+11283
-4053
lines changed

package-lock.json

+8,666-3,979
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
"@stripe/stripe-js": "^1.36.0",
1313
"@svgr/cli": "^5.5.0",
1414
"@woocommerce/woocommerce-rest-api": "^1.0.1",
15+
"@wordpress/base-styles": "^4.21.0",
16+
"@wordpress/block-library": "^8.7.0",
1517
"axios": "^0.21.4",
1618
"classnames": "^2.3.1",
1719
"dompurify": "^2.4.0",
@@ -20,6 +22,7 @@
2022
"next": "^12.3.0",
2123
"next-seo": "^5.15.0",
2224
"next-stripe": "^1.0.0-beta.9",
25+
"nprogress": "^0.2.0",
2326
"prop-types": "^15.8.1",
2427
"react": "^18.2.0",
2528
"react-dom": "^18.2.0",

pages/404.js

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* External Link.
3+
*/
4+
import Link from 'next/link';
5+
import axios from 'axios';
6+
7+
/**
8+
* Internal Link.
9+
*/
10+
import { HEADER_FOOTER_ENDPOINT } from '../src/utils/constants/endpoints';
11+
import Layout from '../src/components/layout';
12+
13+
function Error404( { headerFooter } ) {
14+
return (
15+
<Layout headerFooter={ headerFooter || {} } seo={ null }>
16+
<div className="h-almost-screen">
17+
<section className="text-gray-600 body-font">
18+
<div className="container mx-auto flex px-5 py-24 md:flex-row flex-col items-center">
19+
<div className="lg:flex-grow md:w-1/2 lg:pr-24 md:pr-16 flex flex-col md:items-start md:text-left mb-16 md:mb-0 items-center text-center">
20+
<h1 className="title-font sm:text-4xl text-3xl mb-4 font-medium text-gray-900">
21+
Sorry No result found
22+
</h1>
23+
<div className="flex justify-center">
24+
<Link href="/">
25+
<a className="inline-flex text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg">
26+
Back to Home
27+
</a>
28+
</Link>
29+
</div>
30+
</div>
31+
<div className="lg:max-w-lg lg:w-full md:w-1/2 w-5/6">
32+
<img
33+
className="object-cover object-center rounded"
34+
alt="not found"
35+
src="https://dummyimage.com/620x400"
36+
/>
37+
</div>
38+
</div>
39+
</section>
40+
</div>
41+
</Layout>
42+
);
43+
}
44+
45+
export default Error404;
46+
47+
export async function getStaticProps() {
48+
49+
const { data: headerFooterData } = await axios.get( HEADER_FOOTER_ENDPOINT );
50+
51+
return {
52+
props: {
53+
headerFooter: headerFooterData?.data ?? {},
54+
},
55+
};
56+
}

pages/[...slug].js

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/**
2+
* External Dependencies.
3+
*/
4+
import { isArray, isEmpty } from 'lodash';
5+
import { useRouter } from 'next/router';
6+
7+
/**
8+
* Internal Dependencies.
9+
*/
10+
import Layout from '../src/components/layout';
11+
import { FALLBACK, handleRedirectsAndReturnData, isCustomPageUri } from '../src/utils/slug';
12+
import { getFormattedDate, getPathNameFromUrl, sanitize } from '../src/utils/miscellaneous';
13+
import { getPage, getPages, getPost, getPosts } from '../src/utils/blog';
14+
import axios from 'axios';
15+
import { HEADER_FOOTER_ENDPOINT } from '../src/utils/constants/endpoints';
16+
import Image from '../src/components/image';
17+
import PostMeta from '../src/components/post-meta';
18+
19+
const Page = ( { headerFooter, pageData } ) => {
20+
const router = useRouter();
21+
22+
// If the page is not yet generated, this will be displayed
23+
// initially until getStaticProps() finishes running
24+
if ( router.isFallback ) {
25+
return <div>Loading...</div>;
26+
}
27+
28+
return (
29+
<Layout headerFooter={ headerFooter || {} } seo={ pageData?.yoast_head_json ?? {} }>
30+
<div className="mb-8 w-4/5 m-auto">
31+
<figure className="overflow-hidden mb-4">
32+
<Image
33+
sourceUrl={ pageData?._embedded[ 'wp:featuredmedia' ]?.[ 0 ]?.source_url ?? '' }
34+
title={ pageData?.title?.rendered ?? '' }
35+
width="600"
36+
height="400"
37+
layout="fill"
38+
containerClassNames="w-full h-600px"
39+
/>
40+
</figure>
41+
<PostMeta date={ getFormattedDate( pageData?.date ?? '' ) } authorName={ pageData?._embedded?.author?.[0]?.name ?? '' }/>
42+
<h1 dangerouslySetInnerHTML={ { __html: sanitize( pageData?.title?.rendered ?? '' ) } }/>
43+
<div dangerouslySetInnerHTML={ { __html: sanitize( pageData?.content?.rendered ?? '' ) } }/>
44+
</div>
45+
</Layout>
46+
);
47+
};
48+
49+
export default Page;
50+
51+
export async function getStaticProps( { params } ) {
52+
53+
const { data: headerFooterData } = await axios.get( HEADER_FOOTER_ENDPOINT );
54+
const pageData = await getPage( params?.slug.pop() ?? '' );
55+
56+
const defaultProps = {
57+
props: {
58+
headerFooter: headerFooterData?.data ?? {},
59+
pageData: pageData?.[0] ?? {}
60+
},
61+
/**
62+
* Revalidate means that if a new request comes to server, then every 1 sec it will check
63+
* if the data is changed, if it is changed then it will update the
64+
* static file inside .next folder with the new data, so that any 'SUBSEQUENT' requests should have updated data.
65+
*/
66+
revalidate: 1,
67+
};
68+
69+
return handleRedirectsAndReturnData( defaultProps, pageData );
70+
}
71+
72+
/**
73+
* Since the page name uses catch-all routes,
74+
* for example [...slug],
75+
* that's why params would contain slug which is an array.
76+
* For example, If we need to have dynamic route '/foo/bar'
77+
* Then we would add paths: [ params: { slug: ['foo', 'bar'] } } ]
78+
* Here slug will be an array is ['foo', 'bar'], then Next.js will statically generate the page at /foo/bar
79+
*
80+
* At build time next js will make an api call get the data and
81+
* generate a page bar.js inside .next/foo directory, so when the page is served on browser
82+
* data is already present, unlike getInitialProps which gets the page at build time but makes an api
83+
* call after page is served on the browser.
84+
*
85+
* @see https://nextjs.org/docs/basic-features/data-fetching#the-paths-key-required
86+
*
87+
* @returns {Promise<{paths: [], fallback: boolean}>}
88+
*/
89+
export async function getStaticPaths() {
90+
const pagesData = await getPages();
91+
92+
const pathsData = [];
93+
94+
isArray( pagesData ) && pagesData.map( page => {
95+
96+
/**
97+
* Extract pathname from url.
98+
* e.g.
99+
* getPathNameFromUrl( 'https://example.com/hello/hi/' ) will return '/hello/hi'
100+
* getPathNameFromUrl( 'https://example.com' ) will return '/'
101+
* @type {String}
102+
*/
103+
const pathName = getPathNameFromUrl( page?.link ?? '' );
104+
105+
// Build paths data.
106+
if ( ! isEmpty( pathName ) && ! isCustomPageUri( pathName ) ) {
107+
const slugs = pathName?.split( '/' ).filter( pageSlug => pageSlug );
108+
pathsData.push( { params: { slug: slugs } } );
109+
}
110+
} );
111+
112+
return {
113+
paths: pathsData,
114+
fallback: FALLBACK,
115+
};
116+
}

pages/_app.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
import '../src/styles/index.scss'
1+
import '../src/styles/index.scss';
2+
3+
import Router from 'next/router';
4+
import NProgress from 'nprogress';
5+
6+
NProgress.configure( { showSpinner: false } );
7+
Router.events.on( 'routeChangeStart', () => NProgress.start() );
8+
Router.events.on( 'routeChangeComplete', () => NProgress.done() );
9+
Router.events.on( 'routeChangeError', () => NProgress.done() );
210

311
function MyApp({ Component, pageProps }) {
412
return <Component {...pageProps} />

pages/blog/[slug].js

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/**
2+
* External Dependencies.
3+
*/
4+
import { isEmpty } from 'lodash';
5+
import { useRouter } from 'next/router';
6+
import axios from 'axios';
7+
8+
/**
9+
* Internal Dependencies.
10+
*/
11+
import Layout from '../../src/components/layout';
12+
import { FALLBACK, handleRedirectsAndReturnData } from '../../src/utils/slug';
13+
import { getFormattedDate, sanitize } from '../../src/utils/miscellaneous';
14+
import { HEADER_FOOTER_ENDPOINT } from '../../src/utils/constants/endpoints';
15+
import { getPost, getPosts } from '../../src/utils/blog';
16+
import Image from '../../src/components/image';
17+
import PostMeta from '../../src/components/post-meta';
18+
19+
const Post = ( { headerFooter, postData } ) => {
20+
const router = useRouter();
21+
22+
/**
23+
* If the page is not yet generated, this will be displayed
24+
* initially until getStaticProps() finishes running
25+
*/
26+
if ( router.isFallback ) {
27+
return <div>Loading...</div>;
28+
}
29+
30+
return (
31+
<Layout headerFooter={ headerFooter || {} } seo={ postData?.yoast_head_json ?? {} }>
32+
<div className="mb-8 w-4/5 m-auto">
33+
<figure className="overflow-hidden mb-4">
34+
<Image
35+
sourceUrl={ postData?._embedded[ 'wp:featuredmedia' ]?.[ 0 ]?.source_url ?? '' }
36+
title={ postData?.title?.rendered ?? '' }
37+
width="600"
38+
height="400"
39+
layout="fill"
40+
containerClassNames="w-full h-600px"
41+
/>
42+
</figure>
43+
<PostMeta date={ getFormattedDate( postData?.date ?? '' ) } authorName={ postData?._embedded?.author?.[0]?.name ?? '' }/>
44+
<h1 dangerouslySetInnerHTML={ { __html: sanitize( postData?.title?.rendered ?? '' ) } }/>
45+
<div dangerouslySetInnerHTML={ { __html: sanitize( postData?.content?.rendered ?? '' ) } }/>
46+
</div>
47+
</Layout>
48+
);
49+
};
50+
51+
export default Post;
52+
53+
export async function getStaticProps( { params } ) {
54+
const { data: headerFooterData } = await axios.get( HEADER_FOOTER_ENDPOINT );
55+
const postData = await getPost( params?.slug ?? '' );
56+
57+
const defaultProps = {
58+
props: {
59+
headerFooter: headerFooterData?.data ?? {},
60+
postData: postData?.[0] ?? {}
61+
},
62+
/**
63+
* Revalidate means that if a new request comes to server, then every 1 sec it will check
64+
* if the data is changed, if it is changed then it will update the
65+
* static file inside .next folder with the new data, so that any 'SUBSEQUENT' requests should have updated data.
66+
*/
67+
revalidate: 1,
68+
};
69+
70+
return handleRedirectsAndReturnData( defaultProps, postData );
71+
}
72+
73+
/**
74+
* Since the page name 'does not' use catch-all routes,
75+
* for example [slug],
76+
* that's why params would contain just slug and not an array of slugs , unlike [...slug].
77+
* For example, If we need to have dynamic route '/foo/'
78+
* Then we would add paths: [ params: { slug: 'foo' } } ]
79+
* Here slug will be 'foo', then Next.js will statically generate the page at /foo/
80+
*
81+
* At build time next js will make an api call get the data and
82+
* generate a page bar.js inside .next/foo directory, so when the page is served on browser
83+
* data is already present, unlike getInitialProps which gets the page at build time but makes an api
84+
* call after page is served on the browser.
85+
*
86+
* @see https://nextjs.org/docs/basic-features/data-fetching#the-paths-key-required
87+
*
88+
* @returns {Promise<{paths: [], fallback: boolean}>}
89+
*/
90+
export async function getStaticPaths() {
91+
const { data: postsData } = await getPosts();
92+
93+
const pathsData = [];
94+
95+
postsData?.posts_data.length && postsData?.posts_data.map( post => {
96+
if ( ! isEmpty( post?.slug ) ) {
97+
pathsData.push( { params: { slug: post?.slug } } );
98+
}
99+
} );
100+
101+
return {
102+
paths: pathsData,
103+
fallback: FALLBACK,
104+
};
105+
}

pages/blog/index.js

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* External Dependencies.
3+
*/
4+
import axios from 'axios';
5+
6+
/**
7+
* Internal Dependencies.
8+
*/
9+
import Layout from '../../src/components/layout';
10+
import Posts from '../../src/components/posts';
11+
import Pagination from '../../src/components/pagination';
12+
import { HEADER_FOOTER_ENDPOINT } from '../../src/utils/constants/endpoints';
13+
import { getPosts } from '../../src/utils/blog';
14+
15+
/**
16+
* Blog Component.
17+
*
18+
* @param {Object} headerFooter Header Footer Data.
19+
* @param {Object} postsData Post Data.
20+
*/
21+
const Blog = ( { headerFooter, postsData } ) => {
22+
const seo = {
23+
title: 'Blog Page',
24+
description: 'Blog Page',
25+
og_image: [],
26+
og_site_name: 'React WooCommerce Theme',
27+
robots: {
28+
index: 'index',
29+
follow: 'follow',
30+
},
31+
}
32+
33+
return (
34+
<Layout headerFooter={ headerFooter || {} } seo={ seo }>
35+
<h1>Blog</h1>
36+
<Posts posts={ postsData?.posts_data ?? [] }/>
37+
<Pagination pagesCount={ postsData?.page_count ?? 0 } postName="blog"/>
38+
</Layout>
39+
);
40+
};
41+
42+
export default Blog;
43+
44+
export async function getStaticProps() {
45+
const { data: headerFooterData } = await axios.get( HEADER_FOOTER_ENDPOINT );
46+
const { data: postsData } = await getPosts();
47+
48+
return {
49+
props: {
50+
headerFooter: headerFooterData?.data ?? {},
51+
postsData: postsData || {},
52+
},
53+
54+
/**
55+
* Revalidate means that if a new request comes to server, then every 1 sec it will check
56+
* if the data is changed, if it is changed then it will update the
57+
* static file inside .next folder with the new data, so that any 'SUBSEQUENT' requests should have updated data.
58+
*/
59+
revalidate: 1,
60+
};
61+
}

0 commit comments

Comments
 (0)