From 2dd8d59ae79579927a723e0fcf122d40739a743e Mon Sep 17 00:00:00 2001 From: Joel Varty Date: Fri, 15 Jan 2021 17:37:36 -0500 Subject: [PATCH] v1 convert to Agility --- components/agility-global/AgilityPage.tsx | 23 ++ components/agility-global/ContentZone.js | 35 ++ components/agility-global/GlobalFooter.js | 75 +++++ components/agility-global/GlobalHeader.js | 73 +++++ components/agility-global/Layout.js | 62 ++++ components/agility-global/PreviewBar.js | 60 ++++ .../agility-modules/BestsellingProducts.tsx | 35 ++ .../agility-modules/FeaturedProducts.tsx | 31 ++ components/agility-modules/Hero.tsx | 29 ++ components/agility-modules/ProductDetails.tsx | 23 ++ components/agility-modules/ProductListing.tsx | 8 + components/agility-modules/RichTextArea.tsx | 22 ++ components/agility-modules/index.ts | 30 ++ .../agility-pageTemplates/MainTemplate.tsx | 14 + components/agility-pageTemplates/index.ts | 18 ++ components/ui/Hero/Hero.tsx | 13 +- components/ui/LoadingDots/LoadingDots.tsx | 6 +- framework/agility/agility.config.js | 51 +++ framework/agility/agility.node.ts | 299 ++++++++++++++++++ framework/agility/agility.sync.js | 51 +++ framework/agility/types.ts | 24 ++ framework/agility/utils.js | 108 +++++++ .../api/operations/get-all-pages.ts | 4 +- .../module-data/BestsellingProductsData.ts | 55 ++++ framework/module-data/FeaturedProductsData.ts | 34 ++ framework/module-data/index.ts | 22 ++ jsconfig.json | 5 + next.config.js | 3 +- package.json | 5 +- pages/[...slug].tsx | 92 ++++++ pages/index.tsx | 154 +-------- .../XXXpages.tsx.bak | 0 {pages => pagesBAK}/blog.tsx | 0 {pages => pagesBAK}/cart.tsx | 0 pagesBAK/index.tsx | 152 +++++++++ {pages => pagesBAK}/orders.tsx | 0 {pages => pagesBAK}/product/[slug].tsx | 0 {pages => pagesBAK}/profile.tsx | 0 {pages => pagesBAK}/search.tsx | 0 {pages => pagesBAK}/wishlist.tsx | 0 tsconfig.json | 7 +- yarn.lock | 65 +++- 42 files changed, 1521 insertions(+), 167 deletions(-) create mode 100644 components/agility-global/AgilityPage.tsx create mode 100644 components/agility-global/ContentZone.js create mode 100644 components/agility-global/GlobalFooter.js create mode 100644 components/agility-global/GlobalHeader.js create mode 100644 components/agility-global/Layout.js create mode 100644 components/agility-global/PreviewBar.js create mode 100644 components/agility-modules/BestsellingProducts.tsx create mode 100644 components/agility-modules/FeaturedProducts.tsx create mode 100644 components/agility-modules/Hero.tsx create mode 100644 components/agility-modules/ProductDetails.tsx create mode 100644 components/agility-modules/ProductListing.tsx create mode 100644 components/agility-modules/RichTextArea.tsx create mode 100644 components/agility-modules/index.ts create mode 100644 components/agility-pageTemplates/MainTemplate.tsx create mode 100644 components/agility-pageTemplates/index.ts create mode 100644 framework/agility/agility.config.js create mode 100644 framework/agility/agility.node.ts create mode 100644 framework/agility/agility.sync.js create mode 100644 framework/agility/types.ts create mode 100644 framework/agility/utils.js create mode 100644 framework/module-data/BestsellingProductsData.ts create mode 100644 framework/module-data/FeaturedProductsData.ts create mode 100644 framework/module-data/index.ts create mode 100644 jsconfig.json create mode 100644 pages/[...slug].tsx rename pages/[...pages].tsx => pagesBAK/XXXpages.tsx.bak (100%) rename {pages => pagesBAK}/blog.tsx (100%) rename {pages => pagesBAK}/cart.tsx (100%) create mode 100644 pagesBAK/index.tsx rename {pages => pagesBAK}/orders.tsx (100%) rename {pages => pagesBAK}/product/[slug].tsx (100%) rename {pages => pagesBAK}/profile.tsx (100%) rename {pages => pagesBAK}/search.tsx (100%) rename {pages => pagesBAK}/wishlist.tsx (100%) diff --git a/components/agility-global/AgilityPage.tsx b/components/agility-global/AgilityPage.tsx new file mode 100644 index 000000000..e5c551eba --- /dev/null +++ b/components/agility-global/AgilityPage.tsx @@ -0,0 +1,23 @@ +import pageTemplates from "components/agility-pageTemplates" + +const AgilityPage = (props:any) => { + + if (!props || !props.pageTemplateName) { + console.error(`Page object or template was not found.`) + return null + } + + let AgilityPageTemplate = pageTemplates(props.pageTemplateName) + if (! AgilityPageTemplate) { + console.error(`${props.pageTemplateName} not found.`) + return null + } + + return ( + + ) + + +} + +export default AgilityPage \ No newline at end of file diff --git a/components/agility-global/ContentZone.js b/components/agility-global/ContentZone.js new file mode 100644 index 000000000..70d57f915 --- /dev/null +++ b/components/agility-global/ContentZone.js @@ -0,0 +1,35 @@ +import React, { Component } from 'react'; +import moduleComponents from "components/agility-modules" + + + function ContentZone({ name, page, dynamicPageItem }) { + function RenderModules() { + + if (!page) return null + + let modules = page.zones[name]; + + const modulesToRender = modules.map(m => { + + const AgilityModule = moduleComponents(m.moduleName) + + if (AgilityModule) { + return + } else { + console.error(`React Component for ${m.moduleName} was not found in the Agility Modules list.`) + } + + }) + + return modulesToRender; + } + + + return ( +
+ +
+ ) +} + +export default ContentZone \ No newline at end of file diff --git a/components/agility-global/GlobalFooter.js b/components/agility-global/GlobalFooter.js new file mode 100644 index 000000000..211a9f62a --- /dev/null +++ b/components/agility-global/GlobalFooter.js @@ -0,0 +1,75 @@ +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 new file mode 100644 index 000000000..371f90935 --- /dev/null +++ b/components/agility-global/GlobalHeader.js @@ -0,0 +1,73 @@ +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-global/Layout.js b/components/agility-global/Layout.js new file mode 100644 index 000000000..6a08582ef --- /dev/null +++ b/components/agility-global/Layout.js @@ -0,0 +1,62 @@ +import PreviewBar from './PreviewBar' +import GlobalHeader from './GlobalHeader' +import GlobalFooter from './GlobalFooter' +import { useRouter } from 'next/router' +import Head from 'next/head' +import dynamic from 'next/dynamic' +import tw from "twin.macro" + +const MainElem = tw.main`p-8`; + +import AnimationRevealPage from "helpers/AnimationRevealPage" +import Error from 'next/error' + +function Layout(props) { + const { page, sitemapNode, dynamicPageItem, notFound } = props + + // If the page is not yet generated, this will be displayed + // initially until getStaticProps() finishes running + const router = useRouter() + if (router.isFallback) { + return
Loading page...
+ } + + if (notFound === true) { + return + } + + const AgilityPageTemplate = dynamic(() => import('components/agility-pageTemplates/' + props.pageTemplateName)); + + if (dynamicPageItem?.seo?.metaDescription) { + page.seo.metaDescription = dynamicPageItem.seo.metaDescription + } + + return ( + <> + + {sitemapNode?.title} - Agility CMS Sample Blog + + + + + {dynamicPageItem?.seo?.ogImage && + + } + + + + + + + {/* */} + + + + {/* */} + + + + ) +} + +export default Layout diff --git a/components/agility-global/PreviewBar.js b/components/agility-global/PreviewBar.js new file mode 100644 index 000000000..3ec25f1db --- /dev/null +++ b/components/agility-global/PreviewBar.js @@ -0,0 +1,60 @@ +import React from 'react'; + +const PreviewBar = ({ isPreview, isDevelopmentMode }) => { + + if (isPreview && !isDevelopmentMode) { + return ( +
+ Powered by Agility CMS + You are viewing the latest changes in Preview Mode. +
+ + +
+
+ ) + } else if(isDevelopmentMode) { + return ( +
+ Powered by Agility CMS + You are viewing the latest changes in Development Mode. +
+
+ ) + } else { + return null + } +} + +const exitPreview = () => { + const exit = confirm("Would you like to exit Preview Mode?"); + if (exit === true) { + window.location = `/api/exitPreview?slug=${window.location.pathname}`; + } +} + +const getPreviewLink = () => { + const xhr = new XMLHttpRequest(); + xhr.onload = function () { + + // Process our return data + if (xhr.status >= 200 && xhr.status < 300) { + // What do when the request is successful + + const previewKey = xhr.responseText; + const previewLink = `${window.location.pathname}?agilitypreviewkey=${escape(previewKey)}`; + + prompt("To share this page in preview mode with others, copy the link below:", previewLink); + + } else { + // What do when the request fails + alert('Could not generate Preview Link. This indicates a problem with the API route that generates a Preview Link.') + } + }; + + // Create and send a GET request + xhr.open('GET', '/api/generatePreviewKey'); + xhr.send(); +} + +export default PreviewBar; \ No newline at end of file diff --git a/components/agility-modules/BestsellingProducts.tsx b/components/agility-modules/BestsellingProducts.tsx new file mode 100644 index 000000000..e5856c89e --- /dev/null +++ b/components/agility-modules/BestsellingProducts.tsx @@ -0,0 +1,35 @@ +import React, { FC } from 'react' + +import { ProductCard } from '@components/product' +import { Grid, Marquee, Hero } from '@components/ui' + +interface Fields { +} + +interface Props { + fields: Fields, + customData: any +} + +const BestsellingProducts:FC = ({fields, customData}) => { + + const bestSelling = customData.bestSelling + + return ( + + {bestSelling.slice(0, 12).map(({ node }:any) => ( + + ))} + + ) +} + +export default BestsellingProducts + diff --git a/components/agility-modules/FeaturedProducts.tsx b/components/agility-modules/FeaturedProducts.tsx new file mode 100644 index 000000000..446f3a67a --- /dev/null +++ b/components/agility-modules/FeaturedProducts.tsx @@ -0,0 +1,31 @@ +import { FC } from "react" +import { Grid, Marquee, Hero } from '@components/ui' +import { ProductCard } from '@components/product' + +interface Fields { +} + +interface Props { + fields: Fields, + customData: any +} + +const FeaturedProducts:FC = ({fields, customData}) => { + const featured:any = customData.featured + + return ( + + {featured.map(({ node }:any, i:number) => ( + + ))} + + ) +} + +export default FeaturedProducts + diff --git a/components/agility-modules/Hero.tsx b/components/agility-modules/Hero.tsx new file mode 100644 index 000000000..ebdcf9764 --- /dev/null +++ b/components/agility-modules/Hero.tsx @@ -0,0 +1,29 @@ +import React, { FC } from 'react' +import { Hero } from '@components/ui' +import * as AgilityTypes from "@agility/types" + + +interface Fields { + title:string, + description:string + cTA?:AgilityTypes.URLField +} + +interface Props { + fields: Fields + } + +const HeroModule:FC = ({fields}) => { + + return ( + + ) +} + +export default HeroModule + diff --git a/components/agility-modules/ProductDetails.tsx b/components/agility-modules/ProductDetails.tsx new file mode 100644 index 000000000..414266957 --- /dev/null +++ b/components/agility-modules/ProductDetails.tsx @@ -0,0 +1,23 @@ +import React, { FC } from 'react' +import { Hero } from '@components/ui' +import * as AgilityTypes from "@agility/types" +import { GetProductResult } from '@framework/api/operations/get-product' +import { ProductView } from '@components/product' + +interface Fields { +} + +interface Props { + fields: Fields, + dynamicPageItem:any + } + +const HeroModule:FC = ({fields, dynamicPageItem}) => { + + return ( + + ) +} + +export default HeroModule + diff --git a/components/agility-modules/ProductListing.tsx b/components/agility-modules/ProductListing.tsx new file mode 100644 index 000000000..fd26aa789 --- /dev/null +++ b/components/agility-modules/ProductListing.tsx @@ -0,0 +1,8 @@ +const ProductListing = () => { + return ( +
ProductListing
+ ) +} + +export default ProductListing + diff --git a/components/agility-modules/RichTextArea.tsx b/components/agility-modules/RichTextArea.tsx new file mode 100644 index 000000000..c37332644 --- /dev/null +++ b/components/agility-modules/RichTextArea.tsx @@ -0,0 +1,22 @@ +import React, {FC} from 'react'; +import { Text, Container } from '@components/ui' + +interface Fields { + textblob:string, +} + +interface Props { + fields: Fields +} + +const RichTextArea:FC = ({fields}) => { + + return ( + + + + ); + +} + +export default RichTextArea \ No newline at end of file diff --git a/components/agility-modules/index.ts b/components/agility-modules/index.ts new file mode 100644 index 000000000..79a973d5f --- /dev/null +++ b/components/agility-modules/index.ts @@ -0,0 +1,30 @@ +import {FC} from "react" +import * as AgilityTypes from "@agility/types" +import RichTextArea from "./RichTextArea" +import BestsellingProducts from "./BestsellingProducts" +import ProductDetails from "./ProductDetails" +import FeaturedProducts from "./FeaturedProducts" +import ProductListing from "./ProductListing" +import Hero from "./Hero" + + +const allModules =[ + { name: "RichTextArea", module:RichTextArea }, + { name: "BestsellingProducts", module: BestsellingProducts }, + { name: "FeaturedProducts", module: FeaturedProducts}, + { name: "ProductListing", module: ProductListing}, + { name: "Hero", module: Hero}, + { name: "ProductDetails", module: ProductDetails } +] + +/** + * Find the component for a module. + * @param moduleName + */ +const getModule = (moduleName:string):any | null => { + const obj = allModules.find(m => m.name.toLowerCase() === moduleName.toLowerCase()) + if (!obj) return null + return obj.module +} + +export default getModule diff --git a/components/agility-pageTemplates/MainTemplate.tsx b/components/agility-pageTemplates/MainTemplate.tsx new file mode 100644 index 000000000..413e3a3e9 --- /dev/null +++ b/components/agility-pageTemplates/MainTemplate.tsx @@ -0,0 +1,14 @@ +import React, { Component } from 'react'; +import ContentZone from 'components/agility-global/ContentZone' + +const MainTemplate = (props:any) => { + + return ( +
+ +
+ ); + +} + +export default MainTemplate; \ No newline at end of file diff --git a/components/agility-pageTemplates/index.ts b/components/agility-pageTemplates/index.ts new file mode 100644 index 000000000..6d75e9f5f --- /dev/null +++ b/components/agility-pageTemplates/index.ts @@ -0,0 +1,18 @@ +import MainTemplate from "./MainTemplate" + +interface TemplateObj { + name:string, + template:any +} + +const allTemplates:[TemplateObj] =[ + { name: "MainTemplate", template:MainTemplate } +] + +const getPageTemplate = (templateName:string):any => { + const obj = allTemplates.find(m => m.name.toLowerCase() === templateName.toLowerCase()) + if (! obj) return null + return obj?.template +} + +export default getPageTemplate \ No newline at end of file diff --git a/components/ui/Hero/Hero.tsx b/components/ui/Hero/Hero.tsx index 2e1f124ae..a88fdfe2e 100644 --- a/components/ui/Hero/Hero.tsx +++ b/components/ui/Hero/Hero.tsx @@ -6,10 +6,13 @@ import Link from 'next/link' interface Props { className?: string headline: string - description: string + description: string, + linkText?: string, + linkUrl?: string } -const Hero: FC = ({ headline, description }) => { +const Hero: FC = ({ headline, description, linkText, linkUrl }) => { + return (
@@ -21,12 +24,14 @@ const Hero: FC = ({ headline, description }) => {

{description}

- + { linkText && linkUrl && + - Read it here + {linkText} + }
diff --git a/components/ui/LoadingDots/LoadingDots.tsx b/components/ui/LoadingDots/LoadingDots.tsx index 10e5bbae1..086db067c 100644 --- a/components/ui/LoadingDots/LoadingDots.tsx +++ b/components/ui/LoadingDots/LoadingDots.tsx @@ -3,9 +3,9 @@ import s from './LoadingDots.module.css' const LoadingDots: React.FC = () => { return ( - - - + + + ) } diff --git a/framework/agility/agility.config.js b/framework/agility/agility.config.js new file mode 100644 index 000000000..adb9d7fd5 --- /dev/null +++ b/framework/agility/agility.config.js @@ -0,0 +1,51 @@ +const agilityContentSync = require("@agility/content-sync"); +const agilityFileSystem = require("@agility/content-sync/src/store-interface-filesystem"); + +const agilityConfig = { + guid: process.env.AGILITY_GUID, //Set your guid here + fetchAPIKey: process.env.AGILITY_API_FETCH_KEY, //Set your fetch apikey here + previewAPIKey: process.env.AGILITY_API_PREVIEW_KEY, //set your preview apikey + languageCode: "en-us", //the language for your website in Agility CMS + channelName: "website", //the name of your channel in Agility CMS + securityKey: process.env.AGILITY_SECURITY_KEY, //the website security key used to validate and generate preview keys +}; + +const getSyncClient = ({ isPreview, isDevelopmentMode }) => { + let cachePath = `node_modules/@agility/content-sync/cache/${ + isPreview ? "preview" : "live" + }`; + + if (!isDevelopmentMode) { + //we are in prod and need to use a different directory that Vercel understands + cachePath = `/tmp/agilitycache/${isPreview ? "preview" : "live"}`; + } + + console.log(`Agility CMS => Content cache path is ${cachePath}`); + const apiKey = isPreview + ? agilityConfig.previewAPIKey + : agilityConfig.fetchAPIKey; + + if (!agilityConfig.guid) { + console.log("Agility CMS => No GUID was provided."); + return null; + } + + return agilityContentSync.getSyncClient({ + guid: agilityConfig.guid, + apiKey: apiKey, + isPreview: isPreview, + languages: [agilityConfig.languageCode], + channels: [agilityConfig.channelName], + store: { + interface: agilityFileSystem, + options: { + rootPath: cachePath, + }, + }, + }); +}; + +module.exports = { + agilityConfig, + getSyncClient, +}; diff --git a/framework/agility/agility.node.ts b/framework/agility/agility.node.ts new file mode 100644 index 000000000..62d29c0ab --- /dev/null +++ b/framework/agility/agility.node.ts @@ -0,0 +1,299 @@ +import crypto from 'crypto' +import { asyncForEach } from "./utils" + +import { ModuleWithInit } from "@agility/types" + +//Agility API stuff +import { agilityConfig, getSyncClient } from './agility.config' +import GlobalFooter from 'components/agility-global/GlobalFooter' +import GlobalHeader from 'components/agility-global/GlobalHeader' + +import moduleInitializers from "framework/module-data" + + +const securityKey = agilityConfig.securityKey +const channelName = agilityConfig.channelName +const languageCode = agilityConfig.languageCode +const isDevelopmentMode = process.env.NODE_ENV === "development" + +interface AgilityPageProps { + sitemapNode?: any, + page?: any, + dynamicPageItem?: any, + pageTemplateName?:string|null, + globalHeaderProps?:any, + globalFooterProps?:any, + languageCode?:string|null, + channelName?:string|null, + isPreview?:boolean, + isDevelopmentMode?:boolean, + notFound?:boolean +} + +const getAgilityPageProps = async ({ params, preview, locale }:any):Promise => { + + let path = '/'; + if (params) { + //build path by iterating through slugs + path = ''; + params.slug.map((slug: string) => { + path += '/' + slug + }) + } + + //TODO: use locale to determin LANGUAGECODE (pulled from config at this point...) + + //determine if we are in preview mode + const isPreview:boolean = (preview || isDevelopmentMode); + + + + const agilitySyncClient = getSyncClient({ + isPreview: isPreview, + isDevelopmentMode + }); + + + //always sync to get latest + + + if (! agilitySyncClient) { + console.log("Agility CMS => Sync client could not be accessed.") + return { + notFound: true + }; + } + + if (!isDevelopmentMode) { + console.log(`Agility CMS => Syncing ${isPreview ? "Preview" : "Live"} Mode`) + await agilitySyncClient.runSync(); + } + + console.log(`Agility CMS => Getting page props for '${path}'...`); + + //get sitemap + const sitemap = await agilitySyncClient.store.getSitemap({ channelName, languageCode }); + + if (sitemap === null) { + console.warn("No sitemap found after sync."); + } + + let pageInSitemap = sitemap[path]; + let page: any = null; + let dynamicPageItem: any = null; + + if (path === '/') { + let firstPagePathInSitemap = Object.keys(sitemap)[0]; + pageInSitemap = sitemap[firstPagePathInSitemap]; + } + + if (pageInSitemap) { + //get the page + page = await agilitySyncClient.store.getPage({ + pageID: pageInSitemap.pageID, + languageCode: languageCode + }); + + } else { + //Could not find page + console.warn('page [' + path + '] not found in sitemap.'); + return handlePageNotFound(); + } + + if (!page) { + console.warn('page [' + path + '] not found in getpage method.'); + } + + + //if there is a dynamic page content id on this page, grab it... + if (pageInSitemap.contentID > 0) { + dynamicPageItem = await agilitySyncClient.store.getContentItem({ + contentID: pageInSitemap.contentID, + languageCode: languageCode + }); + } + + //resolve the page template + const pageTemplateName = page.templateName.replace(/[^0-9a-zA-Z]/g, ''); + + //resolve the modules per content zone + await asyncForEach(Object.keys(page.zones), async (zoneName: string) => { + + let modules: { moduleName: string; item: any }[] = []; + + //grab the modules for this content zone + const modulesForThisContentZone = page.zones[zoneName]; + + //loop through the zone's modules + await asyncForEach(modulesForThisContentZone, async (moduleItem: { module: string, item: any }) => { + + //find the react component to use for the module + const moduleInitializer = moduleInitializers(moduleItem.module) + + if (moduleInitializer) { + //resolve any additional data for the modules + + //we have some additional data in the module we'll need, execute that method now, so it can be included in SSG + if (isDevelopmentMode) { + console.log(`Agility CMS => Fetching additional data via getCustomInitialProps for ${moduleItem.module}...`); + } + + const moduleData = await moduleInitializer({ + item: moduleItem.item, + agility: agilitySyncClient.store, + languageCode, + channelName, + pageInSitemap, + dynamicPageItem + }); + + //if we have additional module data, then add it to the module props using 'customData' + if (moduleData != null) { + moduleItem.item.customData = moduleData; + } + } + + modules.push({ + moduleName: moduleItem.module, + item: moduleItem.item, + }) + }) + + + //store as dictionary + page.zones[zoneName] = modules; + + }) + + //resolve data for other shared components + const globalHeaderProps = await GlobalHeader.getCustomInitialProps({ agility: agilitySyncClient.store, languageCode: languageCode, channelName: channelName }); + const globalFooterProps = await GlobalFooter.getCustomInitialProps({ agility: agilitySyncClient.store, languageCode: languageCode, channelName: channelName }); + + return { + sitemapNode: pageInSitemap, + page, + dynamicPageItem, + pageTemplateName, + globalHeaderProps, + globalFooterProps, + languageCode, + channelName, + isPreview, + isDevelopmentMode + } +} + +const getAgilityPaths = async () => { + + console.log(`Agility CMS => Fetching sitemap for getAgilityPaths...`); + + //determine if we are in preview mode + const isPreview = isDevelopmentMode; + + const agilitySyncClient = getSyncClient({ + isPreview, + isDevelopmentMode + }); + + //always sync to get latest + + if (! agilitySyncClient) { + console.log("Agility CMS => Sync client could not be accessed.") + return []; + } + if (!isDevelopmentMode) { + console.log(`Agility CMS => Syncing ${isPreview ? "Preview" : "Live"} Mode`) + await agilitySyncClient.runSync(); + } + + const sitemapFlat = await agilitySyncClient.store.getSitemap({ + channelName, + languageCode + }) + + + + if (!sitemapFlat) { + console.warn("Agility CMS => No Site map found. Make sure your environment variables are setup correctly.") + return [] + } + + + + //returns an array of paths as a string (i.e. ['/home', '/posts'] ) + const paths = Object.keys(sitemapFlat) + .filter(path => { + const sitemapNode = sitemapFlat[path] + return !sitemapNode.redirect + && !sitemapNode.isFolder + }) + + return paths +} + + +const validatePreview = async({ agilityPreviewKey, slug }: any) => { + //Validate the preview key + if (!agilityPreviewKey) { + return { + error: true, + message: `Missing agilitypreviewkey.` + } + } + + //sanitize incoming key (replace spaces with '+') + if (agilityPreviewKey.indexOf(` `) > -1) { + agilityPreviewKey = agilityPreviewKey.split(` `).join(`+`); + } + + //compare the preview key being used + const correctPreviewKey = generatePreviewKey(); + + if (agilityPreviewKey !== correctPreviewKey) { + return { + error: true, + message: `Invalid agilitypreviewkey.` + //message: `Invalid agilitypreviewkey. Incoming key is=${agilityPreviewKey} compared to=${correctPreviewKey}...` + } + } + + //return success + return { + error: false, + message: null + } + +} + +const generatePreviewKey =() => { + //the string we want to encode + const str = `-1_${securityKey}_Preview`; + + //build our byte array + let data = []; + for (var i = 0; i < str.length; ++i) { + data.push(str.charCodeAt(i)); + data.push(0); + } + + //convert byte array to buffer + const strBuffer = Buffer.from(data); + + //encode it! + const previewKey = crypto.createHash('sha512').update(strBuffer).digest('base64'); + return previewKey; +} + +function handlePageNotFound() { + return { + notFound: true + } +} + +export { + getAgilityPageProps, + getAgilityPaths, + validatePreview, + generatePreviewKey +} + diff --git a/framework/agility/agility.sync.js b/framework/agility/agility.sync.js new file mode 100644 index 000000000..593b0ddc4 --- /dev/null +++ b/framework/agility/agility.sync.js @@ -0,0 +1,51 @@ +/* +THIS FILE IS ONLY EXECUTED LOCALLY +WHEN DOING A LOCAL SYNC ON DEMAND +IN DEVELOPMENT MODE +*/ + +require("dotenv").config({ + path: `.env.local`, +}) + +const { getSyncClient } = require('./agility.config') + + +const runSync = async () => { + + const agilitySyncClient = getSyncClient({ isPreview: true, isDevelopmentMode: true }) + if (! agilitySyncClient) { + console.log("Agility CMS => Sync client could not be accessed.") + return; + } + + await agilitySyncClient.runSync(); +} + +const clearSync = async () => { + + const agilitySyncClient = getSyncClient({ isPreview: true, isDevelopmentMode: true }) + if (! agilitySyncClient) { + console.log("Agility CMS => Sync client could not be accessed.") + return; + } + await agilitySyncClient.clearSync(); + +} + + +if (process.argv[2]) { + if (process.argv[2] === "clear") { + //clear everything + return clearSync(); + } else if (process.argv[2] === "sync") { + //run the sync + return runSync() + + } +} + +module.exports = { + clearSync, + runSync +} \ No newline at end of file diff --git a/framework/agility/types.ts b/framework/agility/types.ts new file mode 100644 index 000000000..17895cb3b --- /dev/null +++ b/framework/agility/types.ts @@ -0,0 +1,24 @@ +import { FC } from 'react' + +export interface CustomInitPropsArg { + item:any, + agility:any, + languageCode:any, + channelName:any, + pageInSitemap:any, + dynamicPageItem?:any +} + +export interface CustomInitProps { + (props:CustomInitPropsArg): T; +} + +export interface ModuleWithInit extends FC { + getCustomInitialProps:CustomInitProps +} + +export interface URLField { + href:string, + target:string, + text:string +} \ No newline at end of file diff --git a/framework/agility/utils.js b/framework/agility/utils.js new file mode 100644 index 000000000..e18d8c289 --- /dev/null +++ b/framework/agility/utils.js @@ -0,0 +1,108 @@ +const renderHTML = (html) => { + if (!html) return { __html: "" }; + return { __html: cleanHTML(html) }; +} + +const cleanHTML = (html) => { + if (!html) return "" + + //fix '~' in links in HTML + return html.replace(/href="~\//gi, 'href="/') +} + +const asyncForEach = async (array, callback) => { + for (let index = 0; index < array.length; index++) { + await callback(array[index], index, array); + } +} + + +const expandContentList = async ({ agility, contentItems, languageCode, depth }) => { + + asyncForEach(contentItems, async (contentItem) => { + await expandContentItem({agility, contentItem, languageCode, depth}) + }) + +} + +const expandContentItem = async ({ agility, contentItem, languageCode, depth = 1 }) => { + if (!contentItem) return null; + + + + const api = agility; + + if (depth > 0) { + //make this work for the .fields or the .customFields property... + let fields = contentItem.fields; + if (!fields) fields = contentItem.customFields; + + for (const fieldName in fields) { + const fieldValue = fields[fieldName]; + + if (fieldValue.contentid > 0) { + //single linked item + const childItem = await api.getContentItem({ contentID: fieldValue.contentid, languageCode, depth: depth - 1 }); + if (childItem != null) fields[fieldName] = childItem; + } else if (fieldValue.sortids && fieldValue.sortids.split) { + //multi linked item + const sortIDAry = fieldValue.sortids.split(','); + const childItems = []; + for (const childItemID of sortIDAry) { + const childItem = await api.getContentItem({ contentID: childItemID, languageCode, depth: depth - 1 }); + if (childItem != null) childItems.push(childItem); + } + fields[fieldName] = childItems; + } + } + } + return contentItem; +} + +const expandLinkedList = async ({ agility, contentItem, languageCode, fieldName, sortIDField }) => { + if (!contentItem) return null; + + let fieldObj = contentItem.fields[fieldName] + if (! fieldObj) throw Error(`The field ${fieldName} was not found on the content item.`) + + const referenceName = fieldObj.referencename + if (! referenceName) throw Error(`A referencename property was not found on the ${fieldName} value.`) + + //grab the content list... + let listItems = await agility.getContentList({referenceName, languageCode}) + if (listItems?.length > 0) { + let sortIDs = contentItem.fields[sortIDField] + if (sortIDs?.length > 0 && sortIDs?.split) { + //if we have sort ids, assemble the values in the list based on those... + const sortIDAry = sortIDs.split(','); + const sortedItems = []; + for (const idStr of sortIDAry) { + const childContentID = parseInt(idStr) + + const childItemIndex = listItems.findIndex(item => item.contentID === childContentID) + if (childItemIndex >= 0) { + sortedItems.push(listItems[childItemIndex]); + listItems.splice(childItemIndex, 1) + + } + } + + listItems = sortedItems.concat(listItems) + } + } + + contentItem.fields[fieldName] = listItems; + return contentItem; + +} + + + +module.exports = { + renderHTML, + cleanHTML, + asyncForEach, + expandContentItem, + expandContentList, + expandLinkedList +} \ No newline at end of file diff --git a/framework/bigcommerce/api/operations/get-all-pages.ts b/framework/bigcommerce/api/operations/get-all-pages.ts index 9fe83f2a1..21c70c1dc 100644 --- a/framework/bigcommerce/api/operations/get-all-pages.ts +++ b/framework/bigcommerce/api/operations/get-all-pages.ts @@ -35,9 +35,11 @@ async function getAllPages({ >('/v3/content/pages') const pages = (data as RecursiveRequired) ?? [] - return { + const retPages = { pages: preview ? pages : pages.filter((p) => p.is_visible), } + + return retPages } export default getAllPages diff --git a/framework/module-data/BestsellingProductsData.ts b/framework/module-data/BestsellingProductsData.ts new file mode 100644 index 000000000..3e34f59bc --- /dev/null +++ b/framework/module-data/BestsellingProductsData.ts @@ -0,0 +1,55 @@ +import { getConfig } from '@framework/api' +import getAllProducts from '@framework/api/operations/get-all-products' + +import rangeMap from '@lib/range-map' + +const nonNullable = (v: any) => v + +const BestsellingProductsInit = async function ({ item, agility, languageCode, channelName, pageInSitemap, dynamicPageItem }: any) { + //TODO: pass the locale and preview mode as props... + + + const locale = "en-US" + const preview = false + + const config = getConfig({ locale }) + + // Get Best Selling Products + const { products: bestSellingProducts } = await getAllProducts({ + variables: { field: 'bestSellingProducts', first: 6 }, + config, + preview, + }) + + // Get Best Newest Products + const { products: newestProducts } = await getAllProducts({ + variables: { field: 'newestProducts', first: 12 }, + config, + preview, + }) + + // These are the products that are going to be displayed in the landing. + // We prefer to do the computation at buildtime/servertime + const { bestSelling } = (() => { + + // Create a copy of products that we can mutate + const products = [...newestProducts] + // If the lists of featured and best selling products don't have enough + // products, then fill them with products from the products list, this + // is useful for new commerce sites that don't have a lot of products + return { + bestSelling: rangeMap( + 6, + (i) => bestSellingProducts[i] ?? products.shift() + ).filter(nonNullable), + } + })() + + return { + bestSelling + } + + +} + +export default BestsellingProductsInit \ No newline at end of file diff --git a/framework/module-data/FeaturedProductsData.ts b/framework/module-data/FeaturedProductsData.ts new file mode 100644 index 000000000..87e721fb9 --- /dev/null +++ b/framework/module-data/FeaturedProductsData.ts @@ -0,0 +1,34 @@ +import { getConfig } from '@framework/api' +import getAllProducts from '@framework/api/operations/get-all-products' + + + +import rangeMap from '@lib/range-map' + +const nonNullable = (v: any) => v + +const FeaturedProductsInit = async function ({ item, agility, languageCode, channelName, pageInSitemap, dynamicPageItem }: any) { + //TODO: pass the locale and preview mode as props... + + + const locale = "en-US" + const preview = false + + const config = getConfig({ locale }) + + // Get Featured Products + const { products: featuredProducts } = await getAllProducts({ + variables: { field: 'featuredProducts', first: 6 }, + config, + preview, + }) + + + return { + featured: featuredProducts + } + + +} + +export default FeaturedProductsInit \ No newline at end of file diff --git a/framework/module-data/index.ts b/framework/module-data/index.ts new file mode 100644 index 000000000..f240a9682 --- /dev/null +++ b/framework/module-data/index.ts @@ -0,0 +1,22 @@ +import {FC} from "react" +import * as AgilityTypes from "@agility/types" + +import BestsellingProductsData from "./BestsellingProductsData" +import FeaturedProductsData from "./FeaturedProductsData" + +const allModules:any =[ + { name: "BestsellingProducts", init: BestsellingProductsData }, + { name: "FeaturedProducts", init: FeaturedProductsData} +] + +/** + * Find the data method for a module. + * @param moduleName + */ +const getInitMethod = (moduleName:string):any => { + const obj = allModules.find((m: { name: string }) => m.name.toLowerCase() === moduleName.toLowerCase()) + if (!obj) return null + return obj.init +} + +export default getInitMethod diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 000000000..5e480681a --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "baseUrl": "." + } +} \ No newline at end of file diff --git a/next.config.js b/next.config.js index ee2db68bc..7d1751b2d 100644 --- a/next.config.js +++ b/next.config.js @@ -3,8 +3,9 @@ const bundleAnalyzer = require('@next/bundle-analyzer')({ }) module.exports = bundleAnalyzer({ + images: { - domains: ['cdn11.bigcommerce.com'], + domains: ['cdn11.bigcommerce.com', 'cdn.aglty.io'], }, i18n: { locales: ['en-US', 'es'], diff --git a/package.json b/package.json index b90bf4cf8..790de9f12 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,9 @@ "analyze": "BUNDLE_ANALYZE=both yarn build", "find:unused": "next-unused", "generate": "graphql-codegen", - "generate:definitions": "node framework/bigcommerce/scripts/generate-definitions.js" + "generate:definitions": "node framework/bigcommerce/scripts/generate-definitions.js", + "cms-pull": "NODE_ENV=development && node framework/agility/agility.sync.js sync", + "cms-clear": "node framework/agility/agility.sync.js clear" }, "prettier": { "semi": false, @@ -44,6 +46,7 @@ ] }, "dependencies": { + "@agility/content-sync": "^0.1.23", "@reach/portal": "^0.11.2", "@tailwindcss/ui": "^0.6.2", "@vercel/fetch": "^6.1.0", diff --git a/pages/[...slug].tsx b/pages/[...slug].tsx new file mode 100644 index 000000000..99c4c66be --- /dev/null +++ b/pages/[...slug].tsx @@ -0,0 +1,92 @@ +import type { + GetStaticPathsContext, + GetStaticPropsContext, + InferGetStaticPropsType, + +} from 'next' + + + +import { Layout } from '@components/common' +import { missingLocaleInPages } from '@lib/usage-warns' + +import { defaultPageProps } from '@lib/defaults' + +import AgilityPage from "components/agility-global/AgilityPage" + +import { getConfig } from '@framework/api' +import getProduct from '@framework/api/operations/get-product' + +import { getAgilityPageProps, getAgilityPaths } from "framework/agility/agility.node"; +import getAllProductPaths from '@framework/api/operations/get-all-product-paths' + + +export async function getStaticProps({ preview, params, locale }: GetStaticPropsContext<{ slug: string[] }>) { + + let productCode: string | null = null + + //check if this page is a product... + if (params?.slug.length === 2 + && params?.slug[0] === "product") { + productCode = params.slug[1] + params.slug[1] = "product-details" + } + + const page = await getAgilityPageProps({ preview, params, locale }); + + if (productCode) { + const config = getConfig({ locale }) + const { product } = await getProduct({ + variables: { slug: productCode }, + config, + preview, + }) + + if (product !== null) { + page.dynamicPageItem = product + } else { + throw new Error(`Product not found`) + } + } + + const pages = await getAgilityPaths() + + if (!page) { + // We throw to make sure this fails at build time as this is never expected to happen + throw new Error(`Page not found`) + } + + return { + props: { ...defaultPageProps, pages, page }, + revalidate: 60 * 60, // Every hour + } +} + +export async function getStaticPaths({ locales }: GetStaticPathsContext) { + + //get the paths configured in agility + let agilityPaths = await getAgilityPaths() + + //remove product/product-details from the agility paths (special details page...) + agilityPaths = agilityPaths.filter(p => p !== "/product/product-details") + + //get the product paths from the commerce api + const { products } = await getAllProductPaths() + const productPaths = products.map(p => `/product${p.node.path}`) + + const paths = [...agilityPaths, ...productPaths] + + return { + paths, + fallback: true, + } +} + +export default function Pages({ page }: InferGetStaticPropsType) { + + return ( + + ) +} + +Pages.Layout = Layout diff --git a/pages/index.tsx b/pages/index.tsx index ef1c0a96e..d5ffafe88 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,152 +1,2 @@ -import rangeMap from '@lib/range-map' -import { Layout } from '@components/common' -import { ProductCard } from '@components/product' -import { Grid, Marquee, Hero } from '@components/ui' -import HomeAllProductsGrid from '@components/common/HomeAllProductsGrid' -import type { GetStaticPropsContext, InferGetStaticPropsType } from 'next' - -import { getConfig } from '@framework/api' -import getAllProducts from '@framework/api/operations/get-all-products' -import getSiteInfo from '@framework/api/operations/get-site-info' -import getAllPages from '@framework/api/operations/get-all-pages' - -export async function getStaticProps({ - preview, - locale, -}: GetStaticPropsContext) { - const config = getConfig({ locale }) - - // Get Featured Products - const { products: featuredProducts } = await getAllProducts({ - variables: { field: 'featuredProducts', first: 6 }, - config, - preview, - }) - - // Get Best Selling Products - const { products: bestSellingProducts } = await getAllProducts({ - variables: { field: 'bestSellingProducts', first: 6 }, - config, - preview, - }) - - // Get Best Newest Products - const { products: newestProducts } = await getAllProducts({ - variables: { field: 'newestProducts', first: 12 }, - config, - preview, - }) - - const { categories, brands } = await getSiteInfo({ config, preview }) - const { pages } = await getAllPages({ config, preview }) - - // These are the products that are going to be displayed in the landing. - // We prefer to do the computation at buildtime/servertime - const { featured, bestSelling } = (() => { - // Create a copy of products that we can mutate - const products = [...newestProducts] - // If the lists of featured and best selling products don't have enough - // products, then fill them with products from the products list, this - // is useful for new commerce sites that don't have a lot of products - return { - featured: rangeMap(6, (i) => featuredProducts[i] ?? products.shift()) - .filter(nonNullable) - .sort((a, b) => a.node.prices.price.value - b.node.prices.price.value) - .reverse(), - bestSelling: rangeMap( - 6, - (i) => bestSellingProducts[i] ?? products.shift() - ).filter(nonNullable), - } - })() - - return { - props: { - featured, - bestSelling, - newestProducts, - categories, - brands, - pages, - }, - revalidate: 14400, - } -} - -const nonNullable = (v: any) => v - -export default function Home({ - featured, - bestSelling, - brands, - categories, - newestProducts, -}: InferGetStaticPropsType) { - return ( -
- - {featured.slice(0, 3).map(({ node }, i) => ( - - ))} - - - {bestSelling.slice(3, 6).map(({ node }) => ( - - ))} - - - - {featured.slice(3, 6).map(({ node }, i) => ( - - ))} - - - {bestSelling.slice(0, 3).map(({ node }) => ( - - ))} - - -
- ) -} - -Home.Layout = Layout +//this is just a pointer to the catch-all route and logic for all CMS driven pages (i.e. even rootpage is dynamic from the CMS) +export { default, getStaticProps } from './[...slug]'; \ No newline at end of file diff --git a/pages/[...pages].tsx b/pagesBAK/XXXpages.tsx.bak similarity index 100% rename from pages/[...pages].tsx rename to pagesBAK/XXXpages.tsx.bak diff --git a/pages/blog.tsx b/pagesBAK/blog.tsx similarity index 100% rename from pages/blog.tsx rename to pagesBAK/blog.tsx diff --git a/pages/cart.tsx b/pagesBAK/cart.tsx similarity index 100% rename from pages/cart.tsx rename to pagesBAK/cart.tsx diff --git a/pagesBAK/index.tsx b/pagesBAK/index.tsx new file mode 100644 index 000000000..ef1c0a96e --- /dev/null +++ b/pagesBAK/index.tsx @@ -0,0 +1,152 @@ +import rangeMap from '@lib/range-map' +import { Layout } from '@components/common' +import { ProductCard } from '@components/product' +import { Grid, Marquee, Hero } from '@components/ui' +import HomeAllProductsGrid from '@components/common/HomeAllProductsGrid' +import type { GetStaticPropsContext, InferGetStaticPropsType } from 'next' + +import { getConfig } from '@framework/api' +import getAllProducts from '@framework/api/operations/get-all-products' +import getSiteInfo from '@framework/api/operations/get-site-info' +import getAllPages from '@framework/api/operations/get-all-pages' + +export async function getStaticProps({ + preview, + locale, +}: GetStaticPropsContext) { + const config = getConfig({ locale }) + + // Get Featured Products + const { products: featuredProducts } = await getAllProducts({ + variables: { field: 'featuredProducts', first: 6 }, + config, + preview, + }) + + // Get Best Selling Products + const { products: bestSellingProducts } = await getAllProducts({ + variables: { field: 'bestSellingProducts', first: 6 }, + config, + preview, + }) + + // Get Best Newest Products + const { products: newestProducts } = await getAllProducts({ + variables: { field: 'newestProducts', first: 12 }, + config, + preview, + }) + + const { categories, brands } = await getSiteInfo({ config, preview }) + const { pages } = await getAllPages({ config, preview }) + + // These are the products that are going to be displayed in the landing. + // We prefer to do the computation at buildtime/servertime + const { featured, bestSelling } = (() => { + // Create a copy of products that we can mutate + const products = [...newestProducts] + // If the lists of featured and best selling products don't have enough + // products, then fill them with products from the products list, this + // is useful for new commerce sites that don't have a lot of products + return { + featured: rangeMap(6, (i) => featuredProducts[i] ?? products.shift()) + .filter(nonNullable) + .sort((a, b) => a.node.prices.price.value - b.node.prices.price.value) + .reverse(), + bestSelling: rangeMap( + 6, + (i) => bestSellingProducts[i] ?? products.shift() + ).filter(nonNullable), + } + })() + + return { + props: { + featured, + bestSelling, + newestProducts, + categories, + brands, + pages, + }, + revalidate: 14400, + } +} + +const nonNullable = (v: any) => v + +export default function Home({ + featured, + bestSelling, + brands, + categories, + newestProducts, +}: InferGetStaticPropsType) { + return ( +
+ + {featured.slice(0, 3).map(({ node }, i) => ( + + ))} + + + {bestSelling.slice(3, 6).map(({ node }) => ( + + ))} + + + + {featured.slice(3, 6).map(({ node }, i) => ( + + ))} + + + {bestSelling.slice(0, 3).map(({ node }) => ( + + ))} + + +
+ ) +} + +Home.Layout = Layout diff --git a/pages/orders.tsx b/pagesBAK/orders.tsx similarity index 100% rename from pages/orders.tsx rename to pagesBAK/orders.tsx diff --git a/pages/product/[slug].tsx b/pagesBAK/product/[slug].tsx similarity index 100% rename from pages/product/[slug].tsx rename to pagesBAK/product/[slug].tsx diff --git a/pages/profile.tsx b/pagesBAK/profile.tsx similarity index 100% rename from pages/profile.tsx rename to pagesBAK/profile.tsx diff --git a/pages/search.tsx b/pagesBAK/search.tsx similarity index 100% rename from pages/search.tsx rename to pagesBAK/search.tsx diff --git a/pages/wishlist.tsx b/pagesBAK/wishlist.tsx similarity index 100% rename from pages/wishlist.tsx rename to pagesBAK/wishlist.tsx diff --git a/tsconfig.json b/tsconfig.json index 98639f61e..0b220e8d3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,9 +23,12 @@ "@commerce/*": ["framework/commerce/*"], "@commerce": ["framework/commerce"], "@framework/*": ["framework/bigcommerce/*"], - "@framework": ["framework/bigcommerce"] + "@framework": ["framework/bigcommerce"], + "@agility/*": ["framework/agility/*"], + "@agility": ["framework/agility"] + } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js", "pages/XXXpages.tsx.bak"], "exclude": ["node_modules"] } diff --git a/yarn.lock b/yarn.lock index ec5ac7108..e4bcb5dc1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,23 @@ # yarn lockfile v1 +"@agility/content-fetch@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@agility/content-fetch/-/content-fetch-1.0.0.tgz#a945aa1abf21c093a6688494ab5a15d1282774d9" + integrity sha512-cG8XMT4OtlxbuEk7DW4jwe2MK5WuCvu4vClajjOD4DerkkINtWGfvt92XHlcFQ9Az4RmBnOR4/LQMnH3l7AjXg== + dependencies: + axios "^0.18.1" + axios-cache-adapter "^2.4.1" + +"@agility/content-sync@^0.1.23": + version "0.1.23" + resolved "https://registry.yarnpkg.com/@agility/content-sync/-/content-sync-0.1.23.tgz#68c46a0330bbe2aba772d638669c3bf49f8892e5" + integrity sha512-LDw6+nPqI0Lgtt4fzkDcdHtLnvNiuSyhMca2X/I8QuSrtX2asJGbYH3e8YQEq59C2Ocvfn9xrGJyIVj48f5Ukw== + dependencies: + "@agility/content-fetch" "^1.0.0" + dotenv "^8.2.0" + proper-lockfile "^4.1.1" + "@ampproject/toolbox-core@2.7.4", "@ampproject/toolbox-core@^2.7.1-alpha.0": version "2.7.4" resolved "https://registry.yarnpkg.com/@ampproject/toolbox-core/-/toolbox-core-2.7.4.tgz#8355136f16301458ce942acf6c55952c9a415627" @@ -1766,6 +1783,22 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== +axios-cache-adapter@^2.4.1: + version "2.5.0" + resolved "https://registry.yarnpkg.com/axios-cache-adapter/-/axios-cache-adapter-2.5.0.tgz#e68be61e8c1cd7f31e4e7cfcbfd0435404ae67ef" + integrity sha512-YcMPdMoqmSLoZx7A5YD/PdYGuX6/Y9M2tHBhaIXvXrPeGgNnbW7nb3+uArWlT53WGHLfclnu2voMmS7jGXVg6A== + dependencies: + cache-control-esm "1.0.0" + lodash "^4.17.11" + +axios@^0.18.1: + version "0.18.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.1.tgz#ff3f0de2e7b5d180e757ad98000f1081b87bcea3" + integrity sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g== + dependencies: + follow-redirects "1.5.10" + is-buffer "^2.0.2" + babel-plugin-dynamic-import-node@^2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" @@ -2050,6 +2083,11 @@ bytes@3.1.0, bytes@^3.0.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== +cache-control-esm@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/cache-control-esm/-/cache-control-esm-1.0.0.tgz#417647ecf1837a5e74155f55d5a4ae32a84e2581" + integrity sha512-Fa3UV4+eIk4EOih8FTV6EEsVKO0W5XWtNs6FC3InTfVz+EjurjPfDXY5wZDo/lxjDxg5RjNcurLyxEJBcEUx9g== + cacheable-request@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" @@ -2650,7 +2688,7 @@ debounce@^1.2.0: resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.0.tgz#44a540abc0ea9943018dc0eaa95cce87f65cd131" integrity sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg== -debug@3.1.0: +debug@3.1.0, debug@=3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== @@ -3445,6 +3483,13 @@ flatten@^1.0.2: resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.3.tgz#c1283ac9f27b368abc1e36d1ff7b04501a30356b" integrity sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg== +follow-redirects@1.5.10: + version "1.5.10" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" + integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== + dependencies: + debug "=3.1.0" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -3671,7 +3716,7 @@ got@^9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: version "4.2.4" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== @@ -4042,6 +4087,11 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" +is-buffer@^2.0.2: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + is-callable@^1.1.4, is-callable@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" @@ -6085,6 +6135,15 @@ prop-types@15.7.2, prop-types@^15.6.2: object-assign "^4.1.1" react-is "^16.8.1" +proper-lockfile@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-4.1.1.tgz#284cf9db9e30a90e647afad69deb7cb06881262c" + integrity sha512-1w6rxXodisVpn7QYvLk706mzprPTAPCYAqxMvctmPN3ekuRk/kuGkGc82pangZiAt4R3lwSuUzheTTn0/Yb7Zg== + dependencies: + graceful-fs "^4.1.11" + retry "^0.12.0" + signal-exit "^3.0.2" + prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -6484,7 +6543,7 @@ restore-cursor@^3.1.0: onetime "^5.1.0" signal-exit "^3.0.2" -retry@0.12.0: +retry@0.12.0, retry@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=