mirror of
https://github.com/vercel/commerce.git
synced 2025-07-26 19:51:23 +00:00
v1 convert to Agility
This commit is contained in:
23
components/agility-global/AgilityPage.tsx
Normal file
23
components/agility-global/AgilityPage.tsx
Normal file
@@ -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 (
|
||||
<AgilityPageTemplate {...props} />
|
||||
)
|
||||
|
||||
|
||||
}
|
||||
|
||||
export default AgilityPage
|
35
components/agility-global/ContentZone.js
Normal file
35
components/agility-global/ContentZone.js
Normal file
@@ -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 <AgilityModule key={m.item.contentID} page={page} dynamicPageItem={dynamicPageItem} {...m.item} />
|
||||
} else {
|
||||
console.error(`React Component for ${m.moduleName} was not found in the Agility Modules list.`)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
return modulesToRender;
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
<RenderModules />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ContentZone
|
75
components/agility-global/GlobalFooter.js
Normal file
75
components/agility-global/GlobalFooter.js
Normal file
@@ -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 (
|
||||
<div>FOOTER</div>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
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
|
73
components/agility-global/GlobalHeader.js
Normal file
73
components/agility-global/GlobalHeader.js
Normal file
@@ -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 (
|
||||
<div>HEADER</div>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
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
|
62
components/agility-global/Layout.js
Normal file
62
components/agility-global/Layout.js
Normal file
@@ -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 <div>Loading page...</div>
|
||||
}
|
||||
|
||||
if (notFound === true) {
|
||||
return <Error statusCode="404" />
|
||||
}
|
||||
|
||||
const AgilityPageTemplate = dynamic(() => import('components/agility-pageTemplates/' + props.pageTemplateName));
|
||||
|
||||
if (dynamicPageItem?.seo?.metaDescription) {
|
||||
page.seo.metaDescription = dynamicPageItem.seo.metaDescription
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{sitemapNode?.title} - Agility CMS Sample Blog</title>
|
||||
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
|
||||
<meta name="description" content={page.seo.metaDescription} />
|
||||
<meta name="generator" content="Agility CMS" />
|
||||
<meta name="agility_timestamp" content={new Date().toLocaleString()} />
|
||||
{dynamicPageItem?.seo?.ogImage &&
|
||||
<meta property="og:image" content={dynamicPageItem.seo.ogImage} />
|
||||
}
|
||||
<link rel="stylesheet" href="/prose.css" />
|
||||
|
||||
</Head>
|
||||
<PreviewBar {...props} />
|
||||
|
||||
<MainElem>
|
||||
{/* <AnimationRevealPage disabled> */}
|
||||
<GlobalHeader {...props} />
|
||||
<AgilityPageTemplate {...props} />
|
||||
<GlobalFooter {...props} />
|
||||
{/* </AnimationRevealPage> */}
|
||||
</MainElem>
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Layout
|
60
components/agility-global/PreviewBar.js
Normal file
60
components/agility-global/PreviewBar.js
Normal file
@@ -0,0 +1,60 @@
|
||||
import React from 'react';
|
||||
|
||||
const PreviewBar = ({ isPreview, isDevelopmentMode }) => {
|
||||
|
||||
if (isPreview && !isDevelopmentMode) {
|
||||
return (
|
||||
<div className="agility-preview-bar">
|
||||
<img className="agility-preview-bar__logo" src="https://media.agilitycms.com/preview-bar/2018-11/agility-logo-preview-bar-1.png" alt="Powered by Agility CMS" />
|
||||
<span className="agility-preview-bar__text">You are viewing the latest changes in <strong>Preview Mode</strong>.</span>
|
||||
<div>
|
||||
<button className="agility-preview-bar__btn agility-preview-bar__btn-share" title="Click to generate a share-able link" onClick={getPreviewLink}>Share</button>
|
||||
<button className="agility-preview-bar__btn" title="Click to exit preview" onClick={exitPreview}>Exit Preview</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
} else if(isDevelopmentMode) {
|
||||
return (
|
||||
<div className="agility-preview-bar">
|
||||
<img className="agility-preview-bar__logo" src="https://media.agilitycms.com/preview-bar/2018-11/agility-logo-preview-bar-1.png" alt="Powered by Agility CMS" />
|
||||
<span className="agility-preview-bar__text">You are viewing the latest changes in <strong>Development Mode</strong>.</span>
|
||||
<div></div>
|
||||
</div>
|
||||
)
|
||||
} 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;
|
35
components/agility-modules/BestsellingProducts.tsx
Normal file
35
components/agility-modules/BestsellingProducts.tsx
Normal file
@@ -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<Props> = ({fields, customData}) => {
|
||||
|
||||
const bestSelling = customData.bestSelling
|
||||
|
||||
return (
|
||||
<Marquee variant="secondary">
|
||||
{bestSelling.slice(0, 12).map(({ node }:any) => (
|
||||
<ProductCard
|
||||
key={node.path}
|
||||
product={node}
|
||||
variant="slim"
|
||||
imgWidth={320}
|
||||
imgHeight={320}
|
||||
imgLayout="fixed"
|
||||
/>
|
||||
))}
|
||||
</Marquee>
|
||||
)
|
||||
}
|
||||
|
||||
export default BestsellingProducts
|
||||
|
31
components/agility-modules/FeaturedProducts.tsx
Normal file
31
components/agility-modules/FeaturedProducts.tsx
Normal file
@@ -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<Props> = ({fields, customData}) => {
|
||||
const featured:any = customData.featured
|
||||
|
||||
return (
|
||||
<Grid layout="B">
|
||||
{featured.map(({ node }:any, i:number) => (
|
||||
<ProductCard
|
||||
key={node.path}
|
||||
product={node}
|
||||
imgWidth={i === 1 ? 1080 : 540}
|
||||
imgHeight={i === 1 ? 1080 : 540}
|
||||
/>
|
||||
))}
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
export default FeaturedProducts
|
||||
|
29
components/agility-modules/Hero.tsx
Normal file
29
components/agility-modules/Hero.tsx
Normal file
@@ -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<Props> = ({fields}) => {
|
||||
|
||||
return (
|
||||
<Hero
|
||||
headline={fields.title}
|
||||
description={fields.description}
|
||||
linkText={fields.cTA?.text}
|
||||
linkUrl={fields.cTA?.href}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default HeroModule
|
||||
|
23
components/agility-modules/ProductDetails.tsx
Normal file
23
components/agility-modules/ProductDetails.tsx
Normal file
@@ -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<Props> = ({fields, dynamicPageItem}) => {
|
||||
|
||||
return (
|
||||
<ProductView product={dynamicPageItem} />
|
||||
)
|
||||
}
|
||||
|
||||
export default HeroModule
|
||||
|
8
components/agility-modules/ProductListing.tsx
Normal file
8
components/agility-modules/ProductListing.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
const ProductListing = () => {
|
||||
return (
|
||||
<section>ProductListing</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductListing
|
||||
|
22
components/agility-modules/RichTextArea.tsx
Normal file
22
components/agility-modules/RichTextArea.tsx
Normal file
@@ -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<Props> = ({fields}) => {
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Text className="prose prose-sm sm:prose lg:prose-lg xl:prose-xl" html={fields.textblob} />
|
||||
</Container>
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
export default RichTextArea
|
30
components/agility-modules/index.ts
Normal file
30
components/agility-modules/index.ts
Normal file
@@ -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
|
14
components/agility-pageTemplates/MainTemplate.tsx
Normal file
14
components/agility-pageTemplates/MainTemplate.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import React, { Component } from 'react';
|
||||
import ContentZone from 'components/agility-global/ContentZone'
|
||||
|
||||
const MainTemplate = (props:any) => {
|
||||
|
||||
return (
|
||||
<div className="one-column-template">
|
||||
<ContentZone name='MainContentZone' {...props} />
|
||||
</div>
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
export default MainTemplate;
|
18
components/agility-pageTemplates/index.ts
Normal file
18
components/agility-pageTemplates/index.ts
Normal file
@@ -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
|
@@ -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<Props> = ({ headline, description }) => {
|
||||
const Hero: FC<Props> = ({ headline, description, linkText, linkUrl }) => {
|
||||
|
||||
return (
|
||||
<div className="bg-black">
|
||||
<Container>
|
||||
@@ -21,12 +24,14 @@ const Hero: FC<Props> = ({ headline, description }) => {
|
||||
<p className="mt-5 text-xl leading-7 text-accent-2 text-white">
|
||||
{description}
|
||||
</p>
|
||||
<Link href="/blog">
|
||||
{ linkText && linkUrl &&
|
||||
<Link href={linkUrl}>
|
||||
<a className="text-white pt-3 font-bold hover:underline flex flex-row cursor-pointer w-max-content">
|
||||
Read it here
|
||||
{linkText}
|
||||
<RightArrow width="20" heigh="20" className="ml-1" />
|
||||
</a>
|
||||
</Link>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
|
@@ -3,9 +3,9 @@ import s from './LoadingDots.module.css'
|
||||
const LoadingDots: React.FC = () => {
|
||||
return (
|
||||
<span className={s.root}>
|
||||
<span />
|
||||
<span />
|
||||
<span />
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
Reference in New Issue
Block a user