diff --git a/README.md b/README.md index a0a7509ac..94877b7c2 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fcommerceproject-name=next-commerce-agilitycms&repository-name=next-commerce-agilitycms&demo-title=Next.js%20Commerce%20Agility%20CMS&demo-description=An%20all-in-one%20starter%20kit%20for%20high-performance%20e-commerce%20sites%20with%20Agility%20CMS.&demo-url=https%3A%2F%2Fnextjs-commerce-agility-cms.vercel.app/&demo-image=https%3A%2F%2Fbigcommerce-demo-asset-ksvtgfvnd.vercel.app%2Fbigcommerce.png&integration-ids=oac_MuWZiE4jtmQ2ejZQaQ7ncuDT) +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fcommerceproject-name=next-commerce-agilitycms&repository-name=next-commerce-agilitycms&demo-title=Next.js%20Commerce%20with%20Agility%20CMS&demo-description=An%20all-in-one%20starter%20kit%20for%20high-performance%20e-commerce%20sites%20with%20Agility%20CMS.&demo-url=https%3A%2F%2Fnextjs-commerce-agility-cms.vercel.app/&demo-image=https%3A%2F%2Fbigcommerce-demo-asset-ksvtgfvnd.vercel.app%2Fbigcommerce.png&integration-ids=oac_MuWZiE4jtmQ2ejZQaQ7ncuDT) # Next.js Commerce with Agility CMS diff --git a/assets/components.css b/assets/components.css index ebebcc238..513719dec 100644 --- a/assets/components.css +++ b/assets/components.css @@ -1,3 +1,3 @@ .fit { - min-height: calc(100vh - 88px); + min-height: 400px /*calc(100vh - 88px)*/; } diff --git a/components/agility-global/AgilityPage.tsx b/components/agility-global/AgilityPage.tsx index 330cb3634..081469aa9 100644 --- a/components/agility-global/AgilityPage.tsx +++ b/components/agility-global/AgilityPage.tsx @@ -1,21 +1,49 @@ import pageTemplates from "components/agility-pageTemplates" +import Head from 'next/head' -const AgilityPage = ({ agilityProps, error, revalidate }: { agilityProps: any, error?: any, revalidate?: any}) => { +const AgilityPage = ({ agilityProps, error, revalidate }: { agilityProps: any, error?: any, revalidate?: any }) => { if (!agilityProps) { console.error(`Page object or template was not found.`) return null } - let AgilityPageTemplate = pageTemplates(agilityProps.pageTemplateName) - if (! AgilityPageTemplate) { - console.error(`${agilityProps.pageTemplateName} not found.`) - return null + let pageTitle = "Commerce Storefront" + + if (agilityProps.globalData?.sitedata) { + pageTitle = agilityProps.globalData?.sitedata.name } - return ( - - ) + if (agilityProps.notFound === true) { + return ( + <> + + Page Not Found - {pageTitle} + +
Page not found.
+ + ) + } + + if (agilityProps.pageTemplateName) { + + let AgilityPageTemplate = pageTemplates(agilityProps.pageTemplateName) + if (!AgilityPageTemplate) { + console.error(`${agilityProps.pageTemplateName} not found.`) + return null + } + + return ( + <> + + {agilityProps.sitemapNode?.title} - {pageTitle} + + + + ) + } else { + return null + } } diff --git a/components/agility-global/GlobalFooter.js b/components/agility-global/GlobalFooter.js deleted file mode 100644 index 211a9f62a..000000000 --- a/components/agility-global/GlobalFooter.js +++ /dev/null @@ -1,75 +0,0 @@ -import React, { Component, useState } from 'react'; -import Link from 'next/link'; - -import {expandLinkedList} from "@agility/utils" - -const GlobalFooter = (props) => { - const { globalFooterProps } = props; - - return ( -
FOOTER
- ) - -} - -GlobalFooter.getCustomInitialProps = async function ({agility, languageCode, channelName}) { - - const api = agility; - - let contentItem = null; - - //hack - return {} - - try { - //get the global footer - let contentItemList = await api.getContentList({ - referenceName: "globalfooter", - languageCode: languageCode - }); - - if (contentItemList?.length > 0) { - contentItem = contentItemList[0]; - - //resolve the links... - contentItem = await expandLinkedList({ agility, contentItem, languageCode, - fieldName: "column2Links", - sortIDField: "column2SortIDs" - }) - - contentItem = await expandLinkedList({ agility, contentItem, languageCode, - fieldName: "column3Links", - sortIDField: "column3SortIDs" - }) - - contentItem = await expandLinkedList({ agility, contentItem, languageCode, - fieldName: "column4Links", - sortIDField: "column4SortIDs" - }) - - } - - - } catch (error) { - if (console) console.error("Could not load global footer item.", error); - } - - //return a clean object... - return { - siteName: contentItem.fields.siteName, - siteDescription: contentItem.fields.siteDescription, - column2Title: contentItem.fields.column2Title, - column3Title: contentItem.fields.column3Title, - column4Title: contentItem.fields.column4Title, - facebookURL: contentItem.fields.facebookURL, - twitterURL: contentItem.fields.twitterURL, - youTubeURL: contentItem.fields.youTubeURL, - column2Links: contentItem.fields.column2Links.map(link => link.fields.link), - column3Links: contentItem.fields.column3Links.map(link => link.fields.link), - column4Links: contentItem.fields.column4Links.map(link => link.fields.link), - - } -} - - -export default GlobalFooter \ No newline at end of file diff --git a/components/agility-global/GlobalHeader.js b/components/agility-global/GlobalHeader.js deleted file mode 100644 index 371f90935..000000000 --- a/components/agility-global/GlobalHeader.js +++ /dev/null @@ -1,73 +0,0 @@ -import React, { Component, useState } from 'react'; -import Link from 'next/link'; - - -const GlobalHeader = (props) => { - const { globalHeaderProps, sitemapNode, page } = props; - - const globalHeaderItem = globalHeaderProps.contentItem; - let siteName = globalHeaderItem?.fields.siteName || "Agility Starter 2020" - let logo = globalHeaderItem?.fields.logo || nulll - - return ( -
HEADER
- ) - -} - -GlobalHeader.getCustomInitialProps = async function (props) { - - const api = props.agility; - const languageCode = props.languageCode; - const channelName = props.channelName; - let contentItem = null; - let links = []; - - //hack - return {} - - try { - //get the global header - let contentItemList = await api.getContentList({ - referenceName: "globalheader", - languageCode: languageCode - }); - - if (contentItemList && contentItemList.length) { - contentItem = contentItemList[0]; - - } - } catch (error) { - if (console) console.error("Could not load global header item.", error); - } - - - try { - //get the nested sitemap - let sitemap = await api.getSitemapNested({ - channelName: channelName, - languageCode: languageCode, - }); - - //grab the top level links that are visible on menu - links = sitemap - .filter(node => node.visible.menu) - .map(node => { - return { - text: node.menuText || node.title, - path: node.path - } - }) - - } catch (error) { - if (console) console.error("Could not load nested sitemap.", error); - } - - return { - contentItem, - links - } -} - - -export default GlobalHeader \ No newline at end of file diff --git a/components/agility-modules/BlogPostDetails.tsx b/components/agility-modules/BlogPostDetails.tsx new file mode 100644 index 000000000..b40687689 --- /dev/null +++ b/components/agility-modules/BlogPostDetails.tsx @@ -0,0 +1,99 @@ +import React from "react"; +import Head from "next/head"; +import { renderHTML } from "@agility/nextjs"; +import { AgilityImage } from "@agility/nextjs"; +import truncate from "truncate-html"; +import Link from "next/link"; +import Image from "next/image" + +const PostDetails = ({ dynamicPageItem }: any) => { + + // post fields + const post = dynamicPageItem.fields; + + const productJSON = post.product + const product = JSON.parse(productJSON) + + // format date + const dateStr = new Date(post.date).toLocaleDateString(); + + const description = truncate(post.content, { + length: 160, + decodeEntities: true, + stripTags: true, + reserveLastWord: true, + }); + + let imageSrc = post.image?.url || null; + + + // post image alt + let imageAlt = post.image?.label || null; + + + let ogImageSrc = `${imageSrc}?w=1600&h=900` + + let imageHeight = 900 + let imageWidth = 1600 + + + return ( + <> + + + + + + + + + + + + +
+ +
+
+ + + + + {product.name} + + +
+
+ {product.name} +
+
+ {dateStr} +
+

+ {post.title} +

+ +
+
+
+
+ + ); +}; + + +PostDetails.getCustomInitialProps = async () => { + return { + cloud_name: process.env.CLOUDINARY_CLOUD_NAME + } +} + +export default PostDetails; diff --git a/components/agility-modules/BlogPostListing.tsx b/components/agility-modules/BlogPostListing.tsx new file mode 100644 index 000000000..ec501874d --- /dev/null +++ b/components/agility-modules/BlogPostListing.tsx @@ -0,0 +1,87 @@ +import React from "react"; +import Link from "next/link"; +import Image from "next/image" + +import { ModuleWithInit, AgilityImage } from '@agility/nextjs' +import products from "pages/api/catalog/products"; + +interface ICustomData { + + posts: [] + +} + +interface IModule { + +} + +const PostsListing: ModuleWithInit = ({ customData, module, languageCode, isDevelopmentMode, isPreview }) => { + // get posts + const { posts } = customData; + + // set up href for internal links + let href = "/pages/[...slug]"; + + // if there are no posts, display message on frontend + if (posts.length <= 0) { + return ( +
+

No posts available.

+ +
+ ); + } + + return ( +
+ +
+ ); +}; + + + + +export default PostsListing; diff --git a/components/agility-modules/FeaturedProducts.tsx b/components/agility-modules/FeaturedProducts.tsx deleted file mode 100644 index c3b14f6af..000000000 --- a/components/agility-modules/FeaturedProducts.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { FC } from "react" -import { Grid, Marquee, Hero } from '@components/ui' -import { ProductCard } from '@components/product' -import { ModuleWithInit } from "@agility/nextjs" - -interface ICustomData { - products: any -} - -interface IModule { -} - - -const FeaturedProducts: ModuleWithInit = ({ customData }) => { - - if (! customData) { - return
No featured products returned.
- } - - const products:any = customData.products - - return ( - - {products.slice(0, 3).map((product: any, i: number) => ( - - ))} - - ) -} - -export default FeaturedProducts - diff --git a/components/agility-modules/HomeAllProductsGrid.tsx b/components/agility-modules/HomeAllProductsGrid.tsx deleted file mode 100644 index 06665b419..000000000 --- a/components/agility-modules/HomeAllProductsGrid.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React, { FC } from 'react' -import HomeAllProductsGrid from '@components/common/HomeAllProductsGrid' -import { ModuleWithInit } from '@agility/nextjs' -import { ProductCard } from '@components/product' -import { Grid, Marquee, Hero } from '@components/ui' - - -interface ICustomData { - - products: any - -} - -interface IModule { -} - - -const HomeAllProductsGridModule: ModuleWithInit = ({ customData }) => { - - - const products = customData.products - - return ( - - {products.slice(0, 3).map((product: any, i: number) => ( - - ))} - - ) - - -} - -export default HomeAllProductsGridModule - diff --git a/components/agility-modules/ProductListing.tsx b/components/agility-modules/ProductListing.tsx index fd26aa789..41d91a607 100644 --- a/components/agility-modules/ProductListing.tsx +++ b/components/agility-modules/ProductListing.tsx @@ -1,8 +1,44 @@ -const ProductListing = () => { - return ( -
ProductListing
- ) +import React, { FC } from 'react' +import { ModuleWithInit } from '@agility/nextjs' +import { ProductCard } from '@components/product' +import { Grid, Marquee, Hero } from '@components/ui' + + +interface ICustomData { + + products: any + } -export default ProductListing +interface IModule { + numItems: string, + layout?: 'A' | 'B' | 'C' | 'D' | 'normal' + variant?: 'default' | 'filled' +} + + +const ProductListingModule: ModuleWithInit = ( { customData, module, languageCode, isDevelopmentMode, isPreview }) => { + + + const products = customData.products + + return ( + + {products.map((product: any, i: number) => ( + + ))} + + ) + + +} + +export default ProductListingModule diff --git a/components/agility-modules/BestsellingProducts.tsx b/components/agility-modules/ProductMarquee.tsx similarity index 80% rename from components/agility-modules/BestsellingProducts.tsx rename to components/agility-modules/ProductMarquee.tsx index fd130f03e..21d8b97d8 100644 --- a/components/agility-modules/BestsellingProducts.tsx +++ b/components/agility-modules/ProductMarquee.tsx @@ -12,7 +12,7 @@ interface IModule { } -const BestsellingProducts: ModuleWithInit = ({ customData }) => { +const ProductMarqueeModule: ModuleWithInit = ({ customData }) => { const products = customData.products @@ -26,5 +26,5 @@ const BestsellingProducts: ModuleWithInit = ({ customData } -export default BestsellingProducts +export default ProductMarqueeModule diff --git a/components/agility-modules/RichTextArea.tsx b/components/agility-modules/RichTextArea.tsx index 425f0bee3..d30fbc5f5 100644 --- a/components/agility-modules/RichTextArea.tsx +++ b/components/agility-modules/RichTextArea.tsx @@ -8,8 +8,6 @@ interface Fields { const RichTextArea:Module = ({ module: {fields} }) => { - - return ( diff --git a/components/agility-modules/index.ts b/components/agility-modules/index.ts index 112b9ebff..8224bc6d8 100644 --- a/components/agility-modules/index.ts +++ b/components/agility-modules/index.ts @@ -1,21 +1,22 @@ import RichTextArea from "./RichTextArea" -import BestsellingProducts from "./BestsellingProducts" +import ProductMarquee from "./ProductMarquee" import ProductDetails from "./ProductDetails" -import FeaturedProducts from "./FeaturedProducts" + import ProductListing from "./ProductListing" import ProductSearch from "./ProductSearch" import Hero from "./Hero" -import HomeAllProductsGrid from "./HomeAllProductsGrid" +import HomeAllProductsGrid from "./ProductListing" import Cart from "./Cart" import Orders from "./Orders" import Profile from "./Profile" import Wishlist from "./Wishlist" +import BlogPostListing from "./BlogPostListing" +import BlogPostDetails from "./BlogPostDetails" const allModules = [ { name: "RichTextArea", module: RichTextArea }, - { name: "BestsellingProducts", module: BestsellingProducts }, - { name: "FeaturedProducts", module: FeaturedProducts }, + { name: "ProductMarquee", module: ProductMarquee }, { name: "ProductListing", module: ProductListing }, { name: "ProductSearch", module: ProductSearch }, { name: "Hero", module: Hero }, @@ -23,8 +24,11 @@ const allModules = [ { name: "HomeAllProductsGrid", module: HomeAllProductsGrid }, { name: "Cart", module: Cart }, { name: "Orders", module: Orders }, - { name: "Profile", module: Profile}, - { name: "Wishlist", module: Wishlist} + { name: "Profile", module: Profile }, + { name: "Wishlist", module: Wishlist }, + { name: "BlogPostListing", module: BlogPostListing }, + { name: "BlogPostDetails", module: BlogPostDetails } + ] /** diff --git a/components/common/Footer/Footer.tsx b/components/common/Footer/Footer.tsx index d5c2ea390..9ea384668 100644 --- a/components/common/Footer/Footer.tsx +++ b/components/common/Footer/Footer.tsx @@ -10,122 +10,118 @@ import { I18nWidget } from '@components/common' import s from './Footer.module.css' interface Props { - className?: string - children?: any - pages?: Page[] + className?: string + children?: any + pages?: Page[], + agilityProps: any } -const links = [ - { - name: 'Home', - url: '/', - }, -] +const links:[] = [] -const Footer: FC = ({ className, pages }) => { - const { sitePages } = usePages(pages) - const rootClassName = cn(s.root, className) +const Footer: FC = ({ className, pages, agilityProps }) => { + const { sitePages } = usePages(pages) + const rootClassName = cn(s.root, className) - return ( -
- -
- -
-
- {[...links, ...sitePages].map((page) => ( - - - - {page.name} - - - - ))} -
-
-
-
- - - - -
-
-
-
-
- © 2021 ACME, Inc. All rights reserved. -
-
- Created by - - - + const siteData = agilityProps.globalData["sitedata"] - CMS Integration by - - - -
-
-
-
- ) + return ( + + ) } function usePages(pages?: Page[]) { - const { locale } = useRouter() - const sitePages: Page[] = [] + const { locale } = useRouter() + const sitePages: Page[] = [] - if (pages) { - pages.forEach((page) => { - const slug = page.url && getSlug(page.url) - if (!slug) return - if (locale && !slug.startsWith(`${locale}/`)) return - sitePages.push(page) - }) - } + if (pages) { + pages.forEach((page) => { + const slug = page.url && getSlug(page.url) + if (!slug) return + if (locale && !slug.startsWith(`${locale}/`)) return + sitePages.push(page) + }) + } - return { - sitePages: sitePages.sort(bySortOrder), - } + return { + sitePages: sitePages.sort(bySortOrder), + } } // Sort pages by the sort order assigned in the BC dashboard function bySortOrder(a: Page, b: Page) { - return (a.sort_order ?? 0) - (b.sort_order ?? 0) + return (a.sort_order ?? 0) - (b.sort_order ?? 0) } export default Footer diff --git a/components/common/Layout/Layout.tsx b/components/common/Layout/Layout.tsx index ff6d72aaf..e8bbf46de 100644 --- a/components/common/Layout/Layout.tsx +++ b/components/common/Layout/Layout.tsx @@ -45,7 +45,8 @@ const FeatureBar = dynamic( interface Props { pageProps: { pages?: Page[] - categories: Category[] + categories: Category[], + agilityProps: any } } @@ -90,10 +91,14 @@ const SidebarUI: FC = () => { ) : null } -const Layout: FC = ({ - children, - pageProps: { categories = [], ...pageProps }, -}) => { +const Layout: FC = (props) => { + + const { + children, + pageProps: { agilityProps, categories = [], ...pageProps }, + } = props + + const { acceptedCookies, onAcceptCookies } = useAcceptCookies() const { locale = 'en-US' } = useRouter() const navBarlinks = categories.slice(0, 2).map((c) => ({ @@ -104,9 +109,9 @@ const Layout: FC = ({ return (
- +
{children}
-